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.
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.
11 //! A pass that qualifies constness of temporaries in constants,
12 //! static initializers and functions and also drives promotion.
14 //! The Qualif flags below can be used to also provide better
15 //! diagnostics as to why a constant rvalue wasn't promoted.
17 use rustc_data_structures
::bitvec
::BitVector
;
18 use rustc_data_structures
::indexed_set
::IdxSetBuf
;
19 use rustc_data_structures
::indexed_vec
::{IndexVec, Idx}
;
21 use rustc
::hir
::def_id
::DefId
;
22 use rustc
::middle
::const_val
::ConstVal
;
23 use rustc
::traits
::{self, Reveal}
;
24 use rustc
::ty
::{self, TyCtxt, Ty, TypeFoldable}
;
25 use rustc
::ty
::cast
::CastTy
;
26 use rustc
::ty
::maps
::Providers
;
28 use rustc
::mir
::traversal
::ReversePostorder
;
29 use rustc
::mir
::visit
::{LvalueContext, Visitor}
;
30 use rustc
::middle
::lang_items
;
33 use syntax
::feature_gate
::UnstableFeatures
;
34 use syntax_pos
::{Span, DUMMY_SP}
;
40 use transform
::{MirPass, MirSource}
;
41 use super::promote_consts
::{self, Candidate, TempState}
;
45 // Constant containing interior mutability (UnsafeCell).
46 const MUTABLE_INTERIOR
= 1 << 0;
48 // Constant containing an ADT that implements Drop.
49 const NEEDS_DROP
= 1 << 1;
52 const FN_ARGUMENT
= 1 << 2;
54 // Static lvalue or move from a static.
55 const STATIC
= 1 << 3;
57 // Reference to a static.
58 const STATIC_REF
= 1 << 4;
60 // Not constant at all - non-`const fn` calls, asm!,
61 // pointer comparisons, ptr-to-int casts, etc.
62 const NOT_CONST
= 1 << 5;
64 // Refers to temporaries which cannot be promoted as
65 // promote_consts decided they weren't simple enough.
66 const NOT_PROMOTABLE
= 1 << 6;
68 // Borrows of temporaries can be promoted only
69 // if they have none of the above qualifications.
70 const NEVER_PROMOTE
= 0b111_1111;
72 // Const items can only have MUTABLE_INTERIOR
73 // and NOT_PROMOTABLE without producing an error.
74 const CONST_ERROR
= !Qualif
::MUTABLE_INTERIOR
.bits
&
75 !Qualif
::NOT_PROMOTABLE
.bits
;
79 impl<'a
, 'tcx
> Qualif
{
80 /// Remove flags which are impossible for the given type.
81 fn restrict(&mut self, ty
: Ty
<'tcx
>,
82 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
83 param_env
: ty
::ParamEnv
<'tcx
>) {
84 if ty
.is_freeze(tcx
, param_env
, DUMMY_SP
) {
85 *self = *self - Qualif
::MUTABLE_INTERIOR
;
87 if !ty
.needs_drop(tcx
, param_env
) {
88 *self = *self - Qualif
::NEEDS_DROP
;
93 /// What kind of item we are in.
94 #[derive(Copy, Clone, PartialEq, Eq)]
103 impl fmt
::Display
for Mode
{
104 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
106 Mode
::Const
=> write
!(f
, "constant"),
107 Mode
::Static
| Mode
::StaticMut
=> write
!(f
, "static"),
108 Mode
::ConstFn
=> write
!(f
, "constant function"),
109 Mode
::Fn
=> write
!(f
, "function")
114 struct Qualifier
<'a
, 'gcx
: 'a
+'tcx
, 'tcx
: 'a
> {
119 rpo
: ReversePostorder
<'a
, 'tcx
>,
120 tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>,
121 param_env
: ty
::ParamEnv
<'tcx
>,
122 temp_qualif
: IndexVec
<Local
, Option
<Qualif
>>,
123 return_qualif
: Option
<Qualif
>,
125 const_fn_arg_vars
: BitVector
,
126 local_needs_drop
: IndexVec
<Local
, Option
<Span
>>,
127 temp_promotion_state
: IndexVec
<Local
, TempState
>,
128 promotion_candidates
: Vec
<Candidate
>
131 impl<'a
, 'tcx
> Qualifier
<'a
, 'tcx
, 'tcx
> {
132 fn new(tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
136 -> Qualifier
<'a
, 'tcx
, 'tcx
> {
137 let mut rpo
= traversal
::reverse_postorder(mir
);
138 let temps
= promote_consts
::collect_temps(mir
, &mut rpo
);
147 param_env
: tcx
.param_env(def_id
),
148 temp_qualif
: IndexVec
::from_elem(None
, &mir
.local_decls
),
150 qualif
: Qualif
::empty(),
151 const_fn_arg_vars
: BitVector
::new(mir
.local_decls
.len()),
152 local_needs_drop
: IndexVec
::from_elem(None
, &mir
.local_decls
),
153 temp_promotion_state
: temps
,
154 promotion_candidates
: vec
![]
158 // FIXME(eddyb) we could split the errors into meaningful
159 // categories, but enabling full miri would make that
160 // slightly pointless (even with feature-gating).
161 fn not_const(&mut self) {
162 self.add(Qualif
::NOT_CONST
);
163 if self.mode
!= Mode
::Fn
{
164 span_err
!(self.tcx
.sess
, self.span
, E0019
,
165 "{} contains unimplemented expression type", self.mode
);
169 /// Error about extra statements in a constant.
170 fn statement_like(&mut self) {
171 self.add(Qualif
::NOT_CONST
);
172 if self.mode
!= Mode
::Fn
{
173 span_err
!(self.tcx
.sess
, self.span
, E0016
,
174 "blocks in {}s are limited to items and tail expressions",
179 /// Add the given qualification to self.qualif.
180 fn add(&mut self, qualif
: Qualif
) {
181 self.qualif
= self.qualif
| qualif
;
184 /// Add the given type's qualification to self.qualif.
185 fn add_type(&mut self, ty
: Ty
<'tcx
>) {
186 self.add(Qualif
::MUTABLE_INTERIOR
| Qualif
::NEEDS_DROP
);
187 self.qualif
.restrict(ty
, self.tcx
, self.param_env
);
190 /// Within the provided closure, self.qualif will start
191 /// out empty, and its value after the closure returns will
192 /// be combined with the value before the call to nest.
193 fn nest
<F
: FnOnce(&mut Self)>(&mut self, f
: F
) {
194 let original
= self.qualif
;
195 self.qualif
= Qualif
::empty();
200 /// Check if an Lvalue with the current qualifications could
201 /// be consumed, by either an operand or a Deref projection.
202 fn try_consume(&mut self) -> bool
{
203 if self.qualif
.intersects(Qualif
::STATIC
) && self.mode
!= Mode
::Fn
{
204 let msg
= if self.mode
== Mode
::Static
||
205 self.mode
== Mode
::StaticMut
{
206 "cannot refer to other statics by value, use the \
207 address-of operator or a constant instead"
209 "cannot refer to statics by value, use a constant instead"
211 struct_span_err
!(self.tcx
.sess
, self.span
, E0394
, "{}", msg
)
212 .span_label(self.span
, "referring to another static by value")
213 .note("use the address-of operator or a constant instead")
216 // Replace STATIC with NOT_CONST to avoid further errors.
217 self.qualif
= self.qualif
- Qualif
::STATIC
;
218 self.add(Qualif
::NOT_CONST
);
226 /// Assign the current qualification to the given destination.
227 fn assign(&mut self, dest
: &Lvalue
<'tcx
>, location
: Location
) {
228 let qualif
= self.qualif
;
229 let span
= self.span
;
230 let store
= |slot
: &mut Option
<Qualif
>| {
232 span_bug
!(span
, "multiple assignments to {:?}", dest
);
234 *slot
= Some(qualif
);
237 // Only handle promotable temps in non-const functions.
238 if self.mode
== Mode
::Fn
{
239 if let Lvalue
::Local(index
) = *dest
{
240 if self.mir
.local_kind(index
) == LocalKind
::Temp
241 && self.temp_promotion_state
[index
].is_promotable() {
242 debug
!("store to promotable temp {:?}", index
);
243 store(&mut self.temp_qualif
[index
]);
249 // When initializing a local, record whether the *value* being
250 // stored in it needs dropping, which it may not, even if its
251 // type does, e.g. `None::<String>`.
252 if let Lvalue
::Local(local
) = *dest
{
253 if qualif
.intersects(Qualif
::NEEDS_DROP
) {
254 self.local_needs_drop
[local
] = Some(self.span
);
259 Lvalue
::Local(index
) if self.mir
.local_kind(index
) == LocalKind
::Temp
=> {
260 debug
!("store to temp {:?}", index
);
261 store(&mut self.temp_qualif
[index
])
263 Lvalue
::Local(index
) if self.mir
.local_kind(index
) == LocalKind
::ReturnPointer
=> {
264 debug
!("store to return pointer {:?}", index
);
265 store(&mut self.return_qualif
)
268 Lvalue
::Projection(box Projection
{
269 base
: Lvalue
::Local(index
),
270 elem
: ProjectionElem
::Deref
271 }) if self.mir
.local_kind(index
) == LocalKind
::Temp
272 && self.mir
.local_decls
[index
].ty
.is_box()
273 && self.temp_qualif
[index
].map_or(false, |qualif
| {
274 qualif
.intersects(Qualif
::NOT_CONST
)
276 // Part of `box expr`, we should've errored
277 // already for the Box allocation Rvalue.
280 // This must be an explicit assignment.
282 // Catch more errors in the destination.
283 self.visit_lvalue(dest
, LvalueContext
::Store
, location
);
284 self.statement_like();
289 /// Qualify a whole const, static initializer or const fn.
290 fn qualify_const(&mut self) -> (Qualif
, Rc
<IdxSetBuf
<Local
>>) {
291 debug
!("qualifying {} {:?}", self.mode
, self.def_id
);
295 let mut seen_blocks
= BitVector
::new(mir
.basic_blocks().len());
296 let mut bb
= START_BLOCK
;
298 seen_blocks
.insert(bb
.index());
300 self.visit_basic_block_data(bb
, &mir
[bb
]);
302 let target
= match mir
[bb
].terminator().kind
{
303 TerminatorKind
::Goto { target }
|
304 TerminatorKind
::Drop { target, .. }
|
305 TerminatorKind
::Assert { target, .. }
|
306 TerminatorKind
::Call { destination: Some((_, target)), .. }
=> {
310 // Non-terminating calls cannot produce any value.
311 TerminatorKind
::Call { destination: None, .. }
=> {
315 TerminatorKind
::SwitchInt {..}
|
316 TerminatorKind
::DropAndReplace { .. }
|
317 TerminatorKind
::Resume
|
318 TerminatorKind
::GeneratorDrop
|
319 TerminatorKind
::Yield { .. }
|
320 TerminatorKind
::Unreachable
|
321 TerminatorKind
::FalseEdges { .. }
=> None
,
323 TerminatorKind
::Return
=> {
324 // Check for unused values. This usually means
325 // there are extra statements in the AST.
326 for temp
in mir
.temps_iter() {
327 if self.temp_qualif
[temp
].is_none() {
331 let state
= self.temp_promotion_state
[temp
];
332 if let TempState
::Defined { location, uses: 0 }
= state
{
333 let data
= &mir
[location
.block
];
334 let stmt_idx
= location
.statement_index
;
336 // Get the span for the initialization.
337 let source_info
= if stmt_idx
< data
.statements
.len() {
338 data
.statements
[stmt_idx
].source_info
340 data
.terminator().source_info
342 self.span
= source_info
.span
;
344 // Treat this as a statement in the AST.
345 self.statement_like();
349 // Make sure there are no extra unassigned variables.
350 self.qualif
= Qualif
::NOT_CONST
;
351 for index
in mir
.vars_iter() {
352 if !self.const_fn_arg_vars
.contains(index
.index()) {
353 debug
!("unassigned variable {:?}", index
);
354 self.assign(&Lvalue
::Local(index
), Location
{
356 statement_index
: usize::MAX
,
367 Some(target
) if !seen_blocks
.contains(target
.index()) => {
377 self.qualif
= self.return_qualif
.unwrap_or(Qualif
::NOT_CONST
);
379 // Account for errors in consts by using the
380 // conservative type qualification instead.
381 if self.qualif
.intersects(Qualif
::CONST_ERROR
) {
382 self.qualif
= Qualif
::empty();
383 let return_ty
= mir
.return_ty();
384 self.add_type(return_ty
);
388 // Collect all the temps we need to promote.
389 let mut promoted_temps
= IdxSetBuf
::new_empty(self.temp_promotion_state
.len());
391 for candidate
in &self.promotion_candidates
{
393 Candidate
::Ref(Location { block: bb, statement_index: stmt_idx }
) => {
394 match self.mir
[bb
].statements
[stmt_idx
].kind
{
395 StatementKind
::Assign(_
, Rvalue
::Ref(_
, _
, Lvalue
::Local(index
))) => {
396 promoted_temps
.add(&index
);
401 Candidate
::ShuffleIndices(_
) => {}
405 (self.qualif
, Rc
::new(promoted_temps
))
409 /// Accumulates an Rvalue or Call's effects in self.qualif.
410 /// For functions (constant or not), it also records
411 /// candidates for promotion in promotion_candidates.
412 impl<'a
, 'tcx
> Visitor
<'tcx
> for Qualifier
<'a
, 'tcx
, 'tcx
> {
413 fn visit_local(&mut self,
415 _
: LvalueContext
<'tcx
>,
417 match self.mir
.local_kind(local
) {
418 LocalKind
::ReturnPointer
=> {
422 self.add(Qualif
::FN_ARGUMENT
);
425 self.add(Qualif
::NOT_CONST
);
428 if !self.temp_promotion_state
[local
].is_promotable() {
429 self.add(Qualif
::NOT_PROMOTABLE
);
432 if let Some(qualif
) = self.temp_qualif
[local
] {
441 fn visit_lvalue(&mut self,
442 lvalue
: &Lvalue
<'tcx
>,
443 context
: LvalueContext
<'tcx
>,
444 location
: Location
) {
446 Lvalue
::Local(ref local
) => self.visit_local(local
, context
, location
),
447 Lvalue
::Static(ref global
) => {
448 self.add(Qualif
::STATIC
);
450 if self.mode
!= Mode
::Fn
{
451 for attr
in &self.tcx
.get_attrs(global
.def_id
)[..] {
452 if attr
.check_name("thread_local") {
453 span_err
!(self.tcx
.sess
, self.span
, E0625
,
454 "thread-local statics cannot be \
455 accessed at compile-time");
456 self.add(Qualif
::NOT_CONST
);
462 if self.mode
== Mode
::Const
|| self.mode
== Mode
::ConstFn
{
463 span_err
!(self.tcx
.sess
, self.span
, E0013
,
464 "{}s cannot refer to statics, use \
465 a constant instead", self.mode
);
468 Lvalue
::Projection(ref proj
) => {
470 this
.super_lvalue(lvalue
, context
, location
);
472 ProjectionElem
::Deref
=> {
473 if !this
.try_consume() {
477 if this
.qualif
.intersects(Qualif
::STATIC_REF
) {
478 this
.qualif
= this
.qualif
- Qualif
::STATIC_REF
;
479 this
.add(Qualif
::STATIC
);
482 let base_ty
= proj
.base
.ty(this
.mir
, this
.tcx
).to_ty(this
.tcx
);
483 if let ty
::TyRawPtr(_
) = base_ty
.sty
{
484 this
.add(Qualif
::NOT_CONST
);
485 if this
.mode
!= Mode
::Fn
{
486 struct_span_err
!(this
.tcx
.sess
,
488 "raw pointers cannot be dereferenced in {}s",
490 .span_label(this
.span
,
491 "dereference of raw pointer in constant")
497 ProjectionElem
::Field(..) |
498 ProjectionElem
::Index(_
) => {
499 if this
.mode
!= Mode
::Fn
&&
500 this
.qualif
.intersects(Qualif
::STATIC
) {
501 span_err
!(this
.tcx
.sess
, this
.span
, E0494
,
502 "cannot refer to the interior of another \
503 static, use a constant instead");
505 let ty
= lvalue
.ty(this
.mir
, this
.tcx
).to_ty(this
.tcx
);
506 this
.qualif
.restrict(ty
, this
.tcx
, this
.param_env
);
509 ProjectionElem
::ConstantIndex {..}
|
510 ProjectionElem
::Subslice {..}
|
511 ProjectionElem
::Downcast(..) => {
520 fn visit_operand(&mut self, operand
: &Operand
<'tcx
>, location
: Location
) {
522 Operand
::Consume(ref lvalue
) => {
524 this
.super_operand(operand
, location
);
528 // Mark the consumed locals to indicate later drops are noops.
529 if let Lvalue
::Local(local
) = *lvalue
{
530 self.local_needs_drop
[local
] = None
;
533 Operand
::Constant(ref constant
) => {
534 if let Literal
::Value
{
535 value
: &ty
::Const { val: ConstVal::Unevaluated(def_id, _), ty }
536 } = constant
.literal
{
537 // Don't peek inside trait associated constants.
538 if self.tcx
.trait_of_item(def_id
).is_some() {
541 let (bits
, _
) = self.tcx
.at(constant
.span
).mir_const_qualif(def_id
);
543 let qualif
= Qualif
::from_bits(bits
).expect("invalid mir_const_qualif");
546 // Just in case the type is more specific than
547 // the definition, e.g. impl associated const
548 // with type parameters, take it into account.
549 self.qualif
.restrict(ty
, self.tcx
, self.param_env
);
556 fn visit_rvalue(&mut self, rvalue
: &Rvalue
<'tcx
>, location
: Location
) {
557 // Recurse through operands and lvalues.
558 self.super_rvalue(rvalue
, location
);
563 Rvalue
::UnaryOp(UnOp
::Neg
, _
) |
564 Rvalue
::UnaryOp(UnOp
::Not
, _
) |
565 Rvalue
::NullaryOp(NullOp
::SizeOf
, _
) |
566 Rvalue
::CheckedBinaryOp(..) |
567 Rvalue
::Cast(CastKind
::ReifyFnPointer
, ..) |
568 Rvalue
::Cast(CastKind
::UnsafeFnPointer
, ..) |
569 Rvalue
::Cast(CastKind
::ClosureFnPointer
, ..) |
570 Rvalue
::Cast(CastKind
::Unsize
, ..) |
571 Rvalue
::Discriminant(..) => {}
574 // Static lvalues in consts would have errored already,
575 // don't treat length checks as reads from statics.
576 self.qualif
= self.qualif
- Qualif
::STATIC
;
579 Rvalue
::Ref(_
, kind
, ref lvalue
) => {
580 // Static lvalues in consts would have errored already,
581 // only keep track of references to them here.
582 if self.qualif
.intersects(Qualif
::STATIC
) {
583 self.qualif
= self.qualif
- Qualif
::STATIC
;
584 self.add(Qualif
::STATIC_REF
);
587 let ty
= lvalue
.ty(self.mir
, self.tcx
).to_ty(self.tcx
);
588 if kind
== BorrowKind
::Mut
{
589 // In theory, any zero-sized value could be borrowed
590 // mutably without consequences. However, only &mut []
591 // is allowed right now, and only in functions.
592 let allow
= if self.mode
== Mode
::StaticMut
{
593 // Inside a `static mut`, &mut [...] is also allowed.
595 ty
::TyArray(..) | ty
::TySlice(_
) => true,
598 } else if let ty
::TyArray(_
, len
) = ty
.sty
{
599 len
.val
.to_const_int().unwrap().to_u64().unwrap() == 0 &&
600 self.mode
== Mode
::Fn
606 self.add(Qualif
::NOT_CONST
);
607 if self.mode
!= Mode
::Fn
{
608 struct_span_err
!(self.tcx
.sess
, self.span
, E0017
,
609 "references in {}s may only refer \
610 to immutable values", self.mode
)
611 .span_label(self.span
, format
!("{}s require immutable values",
617 // Constants cannot be borrowed if they contain interior mutability as
618 // it means that our "silent insertion of statics" could change
619 // initializer values (very bad).
620 if self.qualif
.intersects(Qualif
::MUTABLE_INTERIOR
) {
621 // Replace MUTABLE_INTERIOR with NOT_CONST to avoid
622 // duplicate errors (from reborrowing, for example).
623 self.qualif
= self.qualif
- Qualif
::MUTABLE_INTERIOR
;
624 self.add(Qualif
::NOT_CONST
);
625 if self.mode
!= Mode
::Fn
{
626 span_err
!(self.tcx
.sess
, self.span
, E0492
,
627 "cannot borrow a constant which may contain \
628 interior mutability, create a static instead");
633 // We might have a candidate for promotion.
634 let candidate
= Candidate
::Ref(location
);
635 if !self.qualif
.intersects(Qualif
::NEVER_PROMOTE
) {
636 // We can only promote direct borrows of temps.
637 if let Lvalue
::Local(local
) = *lvalue
{
638 if self.mir
.local_kind(local
) == LocalKind
::Temp
{
639 self.promotion_candidates
.push(candidate
);
645 Rvalue
::Cast(CastKind
::Misc
, ref operand
, cast_ty
) => {
646 let operand_ty
= operand
.ty(self.mir
, self.tcx
);
647 let cast_in
= CastTy
::from_ty(operand_ty
).expect("bad input type for cast");
648 let cast_out
= CastTy
::from_ty(cast_ty
).expect("bad output type for cast");
649 match (cast_in
, cast_out
) {
650 (CastTy
::Ptr(_
), CastTy
::Int(_
)) |
651 (CastTy
::FnPtr
, CastTy
::Int(_
)) => {
652 self.add(Qualif
::NOT_CONST
);
653 if self.mode
!= Mode
::Fn
{
654 span_err
!(self.tcx
.sess
, self.span
, E0018
,
655 "raw pointers cannot be cast to integers in {}s",
663 Rvalue
::BinaryOp(op
, ref lhs
, _
) => {
664 if let ty
::TyRawPtr(_
) = lhs
.ty(self.mir
, self.tcx
).sty
{
665 assert
!(op
== BinOp
::Eq
|| op
== BinOp
::Ne
||
666 op
== BinOp
::Le
|| op
== BinOp
::Lt
||
667 op
== BinOp
::Ge
|| op
== BinOp
::Gt
||
668 op
== BinOp
::Offset
);
670 self.add(Qualif
::NOT_CONST
);
671 if self.mode
!= Mode
::Fn
{
673 self.tcx
.sess
, self.span
, E0395
,
674 "raw pointers cannot be compared in {}s",
678 "comparing raw pointers in static")
684 Rvalue
::NullaryOp(NullOp
::Box
, _
) => {
685 self.add(Qualif
::NOT_CONST
);
686 if self.mode
!= Mode
::Fn
{
687 struct_span_err
!(self.tcx
.sess
, self.span
, E0010
,
688 "allocations are not allowed in {}s", self.mode
)
689 .span_label(self.span
, format
!("allocation not allowed in {}s", self.mode
))
694 Rvalue
::Aggregate(ref kind
, _
) => {
695 if let AggregateKind
::Adt(def
, ..) = **kind
{
696 if def
.has_dtor(self.tcx
) {
697 self.add(Qualif
::NEEDS_DROP
);
700 if Some(def
.did
) == self.tcx
.lang_items().unsafe_cell_type() {
701 let ty
= rvalue
.ty(self.mir
, self.tcx
);
703 assert
!(self.qualif
.intersects(Qualif
::MUTABLE_INTERIOR
));
710 fn visit_terminator_kind(&mut self,
712 kind
: &TerminatorKind
<'tcx
>,
713 location
: Location
) {
714 if let TerminatorKind
::Call { ref func, ref args, ref destination, .. }
= *kind
{
715 self.visit_operand(func
, location
);
717 let fn_ty
= func
.ty(self.mir
, self.tcx
);
718 let (mut is_shuffle
, mut is_const_fn
) = (false, None
);
719 if let ty
::TyFnDef(def_id
, _
) = fn_ty
.sty
{
720 match self.tcx
.fn_sig(def_id
).abi() {
722 Abi
::PlatformIntrinsic
=> {
723 assert
!(!self.tcx
.is_const_fn(def_id
));
724 match &self.tcx
.item_name(def_id
)[..] {
725 "size_of" | "min_align_of" => is_const_fn
= Some(def_id
),
727 name
if name
.starts_with("simd_shuffle") => {
735 if self.tcx
.is_const_fn(def_id
) {
736 is_const_fn
= Some(def_id
);
742 for (i
, arg
) in args
.iter().enumerate() {
744 this
.visit_operand(arg
, location
);
745 if is_shuffle
&& i
== 2 && this
.mode
== Mode
::Fn
{
746 let candidate
= Candidate
::ShuffleIndices(bb
);
747 if !this
.qualif
.intersects(Qualif
::NEVER_PROMOTE
) {
748 this
.promotion_candidates
.push(candidate
);
750 span_err
!(this
.tcx
.sess
, this
.span
, E0526
,
751 "shuffle indices are not constant");
758 if let Some(def_id
) = is_const_fn
{
759 // find corresponding rustc_const_unstable feature
760 if let Some(&attr
::Stability
{
761 rustc_const_unstable
: Some(attr
::RustcConstUnstable
{
762 feature
: ref feature_name
764 .. }) = self.tcx
.lookup_stability(def_id
) {
766 // We are in a const or static initializer,
767 if self.mode
!= Mode
::Fn
&&
769 // feature-gate is not enabled,
770 !self.tcx
.sess
.features
.borrow()
771 .declared_lib_features
773 .any(|&(ref sym
, _
)| sym
== feature_name
) &&
775 // this doesn't come from a crate with the feature-gate enabled,
776 self.def_id
.is_local() &&
778 // this doesn't come from a macro that has #[allow_internal_unstable]
779 !self.span
.allows_unstable()
781 let mut err
= self.tcx
.sess
.struct_span_err(self.span
,
782 &format
!("`{}` is not yet stable as a const fn",
783 self.tcx
.item_path_str(def_id
)));
785 "in Nightly builds, add `#![feature({})]` \
786 to the crate attributes to enable",
792 self.qualif
= Qualif
::NOT_CONST
;
793 if self.mode
!= Mode
::Fn
{
794 // FIXME(#24111) Remove this check when const fn stabilizes
795 let (msg
, note
) = if let UnstableFeatures
::Disallow
=
796 self.tcx
.sess
.opts
.unstable_features
{
797 (format
!("calls in {}s are limited to \
798 struct and enum constructors",
800 Some("a limited form of compile-time function \
801 evaluation is available on a nightly \
802 compiler via `const fn`"))
804 (format
!("calls in {}s are limited \
805 to constant functions, \
806 struct and enum constructors",
810 let mut err
= struct_span_err
!(self.tcx
.sess
, self.span
, E0015
, "{}", msg
);
811 if let Some(note
) = note
{
812 err
.span_note(self.span
, note
);
818 if let Some((ref dest
, _
)) = *destination
{
819 // Avoid propagating irrelevant callee/argument qualifications.
820 if self.qualif
.intersects(Qualif
::CONST_ERROR
) {
821 self.qualif
= Qualif
::NOT_CONST
;
823 // Be conservative about the returned value of a const fn.
825 let ty
= dest
.ty(self.mir
, tcx
).to_ty(tcx
);
826 self.qualif
= Qualif
::empty();
829 self.assign(dest
, location
);
831 } else if let TerminatorKind
::Drop { location: ref lvalue, .. }
= *kind
{
832 self.super_terminator_kind(bb
, kind
, location
);
834 // Deny *any* live drops anywhere other than functions.
835 if self.mode
!= Mode
::Fn
{
836 // HACK(eddyb) Emulate a bit of dataflow analysis,
837 // conservatively, that drop elaboration will do.
838 let needs_drop
= if let Lvalue
::Local(local
) = *lvalue
{
839 self.local_needs_drop
[local
]
844 if let Some(span
) = needs_drop
{
845 // Double-check the type being dropped, to minimize false positives.
846 let ty
= lvalue
.ty(self.mir
, self.tcx
).to_ty(self.tcx
);
847 if ty
.needs_drop(self.tcx
, self.param_env
) {
848 struct_span_err
!(self.tcx
.sess
, span
, E0493
,
849 "destructors cannot be evaluated at compile-time")
850 .span_label(span
, format
!("{}s cannot evaluate destructors",
857 // Qualify any operands inside other terminators.
858 self.super_terminator_kind(bb
, kind
, location
);
862 fn visit_assign(&mut self,
865 rvalue
: &Rvalue
<'tcx
>,
866 location
: Location
) {
867 self.visit_rvalue(rvalue
, location
);
869 // Check the allowed const fn argument forms.
870 if let (Mode
::ConstFn
, &Lvalue
::Local(index
)) = (self.mode
, dest
) {
871 if self.mir
.local_kind(index
) == LocalKind
::Var
&&
872 self.const_fn_arg_vars
.insert(index
.index()) {
874 // Direct use of an argument is permitted.
875 if let Rvalue
::Use(Operand
::Consume(Lvalue
::Local(local
))) = *rvalue
{
876 if self.mir
.local_kind(local
) == LocalKind
::Arg
{
881 // Avoid a generic error for other uses of arguments.
882 if self.qualif
.intersects(Qualif
::FN_ARGUMENT
) {
883 let decl
= &self.mir
.local_decls
[index
];
884 span_err
!(self.tcx
.sess
, decl
.source_info
.span
, E0022
,
885 "arguments of constant functions can only \
886 be immutable by-value bindings");
892 self.assign(dest
, location
);
895 fn visit_source_info(&mut self, source_info
: &SourceInfo
) {
896 self.span
= source_info
.span
;
899 fn visit_statement(&mut self, bb
: BasicBlock
, statement
: &Statement
<'tcx
>, location
: Location
) {
901 this
.visit_source_info(&statement
.source_info
);
902 match statement
.kind
{
903 StatementKind
::Assign(ref lvalue
, ref rvalue
) => {
904 this
.visit_assign(bb
, lvalue
, rvalue
, location
);
906 StatementKind
::SetDiscriminant { .. }
|
907 StatementKind
::StorageLive(_
) |
908 StatementKind
::StorageDead(_
) |
909 StatementKind
::InlineAsm {..}
|
910 StatementKind
::EndRegion(_
) |
911 StatementKind
::Validate(..) |
912 StatementKind
::Nop
=> {}
917 fn visit_terminator(&mut self,
919 terminator
: &Terminator
<'tcx
>,
920 location
: Location
) {
921 self.nest(|this
| this
.super_terminator(bb
, terminator
, location
));
925 pub fn provide(providers
: &mut Providers
) {
926 *providers
= Providers
{
932 fn mir_const_qualif
<'a
, 'tcx
>(tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
934 -> (u8, Rc
<IdxSetBuf
<Local
>>) {
935 // NB: This `borrow()` is guaranteed to be valid (i.e., the value
936 // cannot yet be stolen), because `mir_validated()`, which steals
937 // from `mir_const(), forces this query to execute before
938 // performing the steal.
939 let mir
= &tcx
.mir_const(def_id
).borrow();
941 if mir
.return_ty().references_error() {
942 tcx
.sess
.delay_span_bug(mir
.span
, "mir_const_qualif: Mir had errors");
943 return (Qualif
::NOT_CONST
.bits(), Rc
::new(IdxSetBuf
::new_empty(0)));
946 let mut qualifier
= Qualifier
::new(tcx
, def_id
, mir
, Mode
::Const
);
947 let (qualif
, promoted_temps
) = qualifier
.qualify_const();
948 (qualif
.bits(), promoted_temps
)
951 pub struct QualifyAndPromoteConstants
;
953 impl MirPass
for QualifyAndPromoteConstants
{
954 fn run_pass
<'a
, 'tcx
>(&self,
955 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
957 mir
: &mut Mir
<'tcx
>) {
958 // There's not really any point in promoting errorful MIR.
959 if mir
.return_ty().references_error() {
960 tcx
.sess
.delay_span_bug(mir
.span
, "QualifyAndPromoteConstants: Mir had errors");
964 if src
.promoted
.is_some() {
968 let def_id
= src
.def_id
;
969 let id
= tcx
.hir
.as_local_node_id(def_id
).unwrap();
970 let mut const_promoted_temps
= None
;
971 let mode
= match tcx
.hir
.body_owner_kind(id
) {
972 hir
::BodyOwnerKind
::Fn
=> {
973 if tcx
.is_const_fn(def_id
) {
979 hir
::BodyOwnerKind
::Const
=> {
980 const_promoted_temps
= Some(tcx
.mir_const_qualif(def_id
).1);
983 hir
::BodyOwnerKind
::Static(hir
::MutImmutable
) => Mode
::Static
,
984 hir
::BodyOwnerKind
::Static(hir
::MutMutable
) => Mode
::StaticMut
,
987 if mode
== Mode
::Fn
|| mode
== Mode
::ConstFn
{
988 // This is ugly because Qualifier holds onto mir,
989 // which can't be mutated until its scope ends.
990 let (temps
, candidates
) = {
991 let mut qualifier
= Qualifier
::new(tcx
, def_id
, mir
, mode
);
992 if mode
== Mode
::ConstFn
{
993 // Enforce a constant-like CFG for `const fn`.
994 qualifier
.qualify_const();
996 while let Some((bb
, data
)) = qualifier
.rpo
.next() {
997 qualifier
.visit_basic_block_data(bb
, data
);
1001 (qualifier
.temp_promotion_state
, qualifier
.promotion_candidates
)
1004 // Do the actual promotion, now that we know what's viable.
1005 promote_consts
::promote_candidates(mir
, tcx
, temps
, candidates
);
1007 let promoted_temps
= if mode
== Mode
::Const
{
1008 // Already computed by `mir_const_qualif`.
1009 const_promoted_temps
.unwrap()
1011 Qualifier
::new(tcx
, def_id
, mir
, mode
).qualify_const().1
1014 // In `const` and `static` everything without `StorageDead`
1015 // is `'static`, we don't have to create promoted MIR fragments,
1016 // just remove `Drop` and `StorageDead` on "promoted" locals.
1017 for block
in mir
.basic_blocks_mut() {
1018 block
.statements
.retain(|statement
| {
1019 match statement
.kind
{
1020 StatementKind
::StorageDead(index
) => {
1021 !promoted_temps
.contains(&index
)
1026 let terminator
= block
.terminator_mut();
1027 match terminator
.kind
{
1028 TerminatorKind
::Drop { location: Lvalue::Local(index), target, .. }
=> {
1029 if promoted_temps
.contains(&index
) {
1030 terminator
.kind
= TerminatorKind
::Goto
{
1040 // Statics must be Sync.
1041 if mode
== Mode
::Static
{
1042 // `#[thread_local]` statics don't have to be `Sync`.
1043 for attr
in &tcx
.get_attrs(def_id
)[..] {
1044 if attr
.check_name("thread_local") {
1048 let ty
= mir
.return_ty();
1049 tcx
.infer_ctxt().enter(|infcx
| {
1050 let param_env
= ty
::ParamEnv
::empty(Reveal
::UserFacing
);
1051 let cause
= traits
::ObligationCause
::new(mir
.span
, id
, traits
::SharedStatic
);
1052 let mut fulfillment_cx
= traits
::FulfillmentContext
::new();
1053 fulfillment_cx
.register_bound(&infcx
,
1056 tcx
.require_lang_item(lang_items
::SyncTraitLangItem
),
1058 if let Err(err
) = fulfillment_cx
.select_all_or_error(&infcx
) {
1059 infcx
.report_fulfillment_errors(&err
, None
);