1 use rustc_attr
as attr
;
3 use rustc_hir
::def_id
::DefId
;
4 use rustc_middle
::mir
::*;
5 use rustc_middle
::ty
::subst
::GenericArgKind
;
6 use rustc_middle
::ty
::{self, adjustment::PointerCast, Ty, TyCtxt}
;
7 use rustc_span
::symbol
::{sym, Symbol}
;
9 use rustc_target
::spec
::abi
::Abi
::RustIntrinsic
;
12 type McfResult
= Result
<(), (Span
, Cow
<'
static, str>)>;
14 pub fn is_min_const_fn(tcx
: TyCtxt
<'tcx
>, def_id
: DefId
, body
: &'a Body
<'tcx
>) -> McfResult
{
15 // Prevent const trait methods from being annotated as `stable`.
16 if tcx
.features().staged_api
{
17 let hir_id
= tcx
.hir().as_local_hir_id(def_id
.expect_local());
18 if crate::const_eval
::is_parent_const_impl_raw(tcx
, hir_id
) {
19 return Err((body
.span
, "trait methods cannot be stable const fn".into()));
23 let mut current
= def_id
;
25 let predicates
= tcx
.predicates_of(current
);
26 for (predicate
, _
) in predicates
.predicates
{
27 match predicate
.kind() {
28 ty
::PredicateKind
::RegionOutlives(_
)
29 | ty
::PredicateKind
::TypeOutlives(_
)
30 | ty
::PredicateKind
::WellFormed(_
)
31 | ty
::PredicateKind
::Projection(_
)
32 | ty
::PredicateKind
::ConstEvaluatable(..)
33 | ty
::PredicateKind
::ConstEquate(..) => continue,
34 ty
::PredicateKind
::ObjectSafe(_
) => {
35 bug
!("object safe predicate on function: {:#?}", predicate
)
37 ty
::PredicateKind
::ClosureKind(..) => {
38 bug
!("closure kind predicate on function: {:#?}", predicate
)
40 ty
::PredicateKind
::Subtype(_
) => {
41 bug
!("subtype predicate on function: {:#?}", predicate
)
43 &ty
::PredicateKind
::Trait(pred
, constness
) => {
44 if Some(pred
.def_id()) == tcx
.lang_items().sized_trait() {
47 match pred
.skip_binder().self_ty().kind
{
49 // Allow `T: ?const Trait`
50 if constness
== hir
::Constness
::NotConst
51 && feature_allowed(tcx
, def_id
, sym
::const_trait_bound_opt_out
)
56 let generics
= tcx
.generics_of(current
);
57 let def
= generics
.type_param(p
, tcx
);
58 let span
= tcx
.def_span(def
.def_id
);
61 "trait bounds other than `Sized` \
62 on const fn parameters are unstable"
66 // other kinds of bounds are either tautologies
67 // or cause errors in other passes
73 match predicates
.parent
{
74 Some(parent
) => current
= parent
,
79 for local
in &body
.local_decls
{
80 check_ty(tcx
, local
.ty
, local
.source_info
.span
, def_id
)?
;
82 // impl trait is gone in MIR, so check the return type manually
85 tcx
.fn_sig(def_id
).output().skip_binder(),
86 body
.local_decls
.iter().next().unwrap().source_info
.span
,
90 for bb
in body
.basic_blocks() {
91 check_terminator(tcx
, body
, def_id
, bb
.terminator())?
;
92 for stmt
in &bb
.statements
{
93 check_statement(tcx
, body
, def_id
, stmt
)?
;
99 fn check_ty(tcx
: TyCtxt
<'tcx
>, ty
: Ty
<'tcx
>, span
: Span
, fn_def_id
: DefId
) -> McfResult
{
100 for arg
in ty
.walk() {
101 let ty
= match arg
.unpack() {
102 GenericArgKind
::Type(ty
) => ty
,
104 // No constraints on lifetimes or constants, except potentially
105 // constants' types, but `walk` will get to them as well.
106 GenericArgKind
::Lifetime(_
) | GenericArgKind
::Const(_
) => continue,
110 ty
::Ref(_
, _
, hir
::Mutability
::Mut
) => {
111 if !feature_allowed(tcx
, fn_def_id
, sym
::const_mut_refs
) {
112 return Err((span
, "mutable references in const fn are unstable".into()));
115 ty
::Opaque(..) => return Err((span
, "`impl Trait` in const fn is unstable".into())),
117 if !tcx
.const_fn_is_allowed_fn_ptr(fn_def_id
) {
118 return Err((span
, "function pointers in const fn are unstable".into()));
121 ty
::Dynamic(preds
, _
) => {
122 for pred
in preds
.iter() {
123 match pred
.skip_binder() {
124 ty
::ExistentialPredicate
::AutoTrait(_
)
125 | ty
::ExistentialPredicate
::Projection(_
) => {
128 "trait bounds other than `Sized` \
129 on const fn parameters are unstable"
133 ty
::ExistentialPredicate
::Trait(trait_ref
) => {
134 if Some(trait_ref
.def_id
) != tcx
.lang_items().sized_trait() {
137 "trait bounds other than `Sized` \
138 on const fn parameters are unstable"
156 rvalue
: &Rvalue
<'tcx
>,
160 Rvalue
::ThreadLocalRef(_
) => {
161 Err((span
, "cannot access thread local storage in const fn".into()))
163 Rvalue
::Repeat(operand
, _
) | Rvalue
::Use(operand
) => {
164 check_operand(tcx
, operand
, span
, def_id
, body
)
167 | Rvalue
::Discriminant(place
)
168 | Rvalue
::Ref(_
, _
, place
)
169 | Rvalue
::AddressOf(_
, place
) => check_place(tcx
, *place
, span
, def_id
, body
),
170 Rvalue
::Cast(CastKind
::Misc
, operand
, cast_ty
) => {
171 use rustc_middle
::ty
::cast
::CastTy
;
172 let cast_in
= CastTy
::from_ty(operand
.ty(body
, tcx
)).expect("bad input type for cast");
173 let cast_out
= CastTy
::from_ty(cast_ty
).expect("bad output type for cast");
174 match (cast_in
, cast_out
) {
175 (CastTy
::Ptr(_
) | CastTy
::FnPtr
, CastTy
::Int(_
)) => {
176 Err((span
, "casting pointers to ints is unstable in const fn".into()))
178 _
=> check_operand(tcx
, operand
, span
, def_id
, body
),
182 CastKind
::Pointer(PointerCast
::MutToConstPointer
| PointerCast
::ArrayToPointer
),
185 ) => check_operand(tcx
, operand
, span
, def_id
, body
),
188 PointerCast
::UnsafeFnPointer
189 | PointerCast
::ClosureFnPointer(_
)
190 | PointerCast
::ReifyFnPointer
,
194 ) => Err((span
, "function pointer casts are not allowed in const fn".into())),
195 Rvalue
::Cast(CastKind
::Pointer(PointerCast
::Unsize
), op
, cast_ty
) => {
196 let pointee_ty
= if let Some(deref_ty
) = cast_ty
.builtin_deref(true) {
199 // We cannot allow this for now.
202 "unsizing casts are only allowed for references right now".into(),
205 let unsized_ty
= tcx
.struct_tail_erasing_lifetimes(pointee_ty
, tcx
.param_env(def_id
));
206 if let ty
::Slice(_
) | ty
::Str
= unsized_ty
.kind
{
207 check_operand(tcx
, op
, span
, def_id
, body
)?
;
208 // Casting/coercing things to slices is fine.
211 // We just can't allow trait objects until we have figured out trait method calls.
212 Err((span
, "unsizing casts are not allowed in const fn".into()))
215 // binops are fine on integers
216 Rvalue
::BinaryOp(_
, lhs
, rhs
) | Rvalue
::CheckedBinaryOp(_
, lhs
, rhs
) => {
217 check_operand(tcx
, lhs
, span
, def_id
, body
)?
;
218 check_operand(tcx
, rhs
, span
, def_id
, body
)?
;
219 let ty
= lhs
.ty(body
, tcx
);
220 if ty
.is_integral() || ty
.is_bool() || ty
.is_char() {
223 Err((span
, "only int, `bool` and `char` operations are stable in const fn".into()))
226 Rvalue
::NullaryOp(NullOp
::SizeOf
, _
) => Ok(()),
227 Rvalue
::NullaryOp(NullOp
::Box
, _
) => {
228 Err((span
, "heap allocations are not allowed in const fn".into()))
230 Rvalue
::UnaryOp(_
, operand
) => {
231 let ty
= operand
.ty(body
, tcx
);
232 if ty
.is_integral() || ty
.is_bool() {
233 check_operand(tcx
, operand
, span
, def_id
, body
)
235 Err((span
, "only int and `bool` operations are stable in const fn".into()))
238 Rvalue
::Aggregate(_
, operands
) => {
239 for operand
in operands
{
240 check_operand(tcx
, operand
, span
, def_id
, body
)?
;
251 statement
: &Statement
<'tcx
>,
253 let span
= statement
.source_info
.span
;
254 match &statement
.kind
{
255 StatementKind
::Assign(box (place
, rval
)) => {
256 check_place(tcx
, *place
, span
, def_id
, body
)?
;
257 check_rvalue(tcx
, body
, def_id
, rval
, span
)
260 StatementKind
::FakeRead(_
, place
) => check_place(tcx
, **place
, span
, def_id
, body
),
262 // just an assignment
263 StatementKind
::SetDiscriminant { place, .. }
=> {
264 check_place(tcx
, **place
, span
, def_id
, body
)
267 StatementKind
::LlvmInlineAsm { .. }
=> {
268 Err((span
, "cannot use inline assembly in const fn".into()))
271 // These are all NOPs
272 StatementKind
::StorageLive(_
)
273 | StatementKind
::StorageDead(_
)
274 | StatementKind
::Retag { .. }
275 | StatementKind
::AscribeUserType(..)
276 | StatementKind
::Nop
=> Ok(()),
282 operand
: &Operand
<'tcx
>,
288 Operand
::Move(place
) | Operand
::Copy(place
) => check_place(tcx
, *place
, span
, def_id
, body
),
289 Operand
::Constant(c
) => match c
.check_static_ptr(tcx
) {
290 Some(_
) => Err((span
, "cannot access `static` items in const fn".into())),
303 let mut cursor
= place
.projection
.as_ref();
304 while let &[ref proj_base @
.., elem
] = cursor
{
307 ProjectionElem
::Field(..) => {
308 let base_ty
= Place
::ty_from(place
.local
, &proj_base
, body
, tcx
).ty
;
309 if let Some(def
) = base_ty
.ty_adt_def() {
310 // No union field accesses in `const fn`
312 if !feature_allowed(tcx
, def_id
, sym
::const_fn_union
) {
313 return Err((span
, "accessing union fields is unstable".into()));
318 ProjectionElem
::ConstantIndex { .. }
319 | ProjectionElem
::Downcast(..)
320 | ProjectionElem
::Subslice { .. }
321 | ProjectionElem
::Deref
322 | ProjectionElem
::Index(_
) => {}
329 /// Returns `true` if the given feature gate is allowed within the function with the given `DefId`.
330 fn feature_allowed(tcx
: TyCtxt
<'tcx
>, def_id
: DefId
, feature_gate
: Symbol
) -> bool
{
331 // All features require that the corresponding gate be enabled,
332 // even if the function has `#[allow_internal_unstable(the_gate)]`.
333 if !tcx
.features().enabled(feature_gate
) {
337 // If this crate is not using stability attributes, or this function is not claiming to be a
338 // stable `const fn`, that is all that is required.
339 if !tcx
.features().staged_api
|| tcx
.has_attr(def_id
, sym
::rustc_const_unstable
) {
343 // However, we cannot allow stable `const fn`s to use unstable features without an explicit
344 // opt-in via `allow_internal_unstable`.
345 attr
::allow_internal_unstable(&tcx
.get_attrs(def_id
), &tcx
.sess
.diagnostic())
346 .map_or(false, |mut features
| features
.any(|name
| name
== feature_gate
))
349 /// Returns `true` if the given library feature gate is allowed within the function with the given `DefId`.
350 pub fn lib_feature_allowed(tcx
: TyCtxt
<'tcx
>, def_id
: DefId
, feature_gate
: Symbol
) -> bool
{
351 // All features require that the corresponding gate be enabled,
352 // even if the function has `#[allow_internal_unstable(the_gate)]`.
353 if !tcx
.features().declared_lib_features
.iter().any(|&(sym
, _
)| sym
== feature_gate
) {
357 // If this crate is not using stability attributes, or this function is not claiming to be a
358 // stable `const fn`, that is all that is required.
359 if !tcx
.features().staged_api
|| tcx
.has_attr(def_id
, sym
::rustc_const_unstable
) {
363 // However, we cannot allow stable `const fn`s to use unstable features without an explicit
364 // opt-in via `allow_internal_unstable`.
365 attr
::allow_internal_unstable(&tcx
.get_attrs(def_id
), &tcx
.sess
.diagnostic())
366 .map_or(false, |mut features
| features
.any(|name
| name
== feature_gate
))
371 body
: &'a Body
<'tcx
>,
373 terminator
: &Terminator
<'tcx
>,
375 let span
= terminator
.source_info
.span
;
376 match &terminator
.kind
{
377 TerminatorKind
::FalseEdge { .. }
378 | TerminatorKind
::FalseUnwind { .. }
379 | TerminatorKind
::Goto { .. }
380 | TerminatorKind
::Return
381 | TerminatorKind
::Resume
382 | TerminatorKind
::Unreachable
=> Ok(()),
384 TerminatorKind
::Drop { place, .. }
=> check_place(tcx
, *place
, span
, def_id
, body
),
385 TerminatorKind
::DropAndReplace { place, value, .. }
=> {
386 check_place(tcx
, *place
, span
, def_id
, body
)?
;
387 check_operand(tcx
, value
, span
, def_id
, body
)
390 TerminatorKind
::SwitchInt { discr, switch_ty: _, values: _, targets: _ }
=> {
391 check_operand(tcx
, discr
, span
, def_id
, body
)
394 TerminatorKind
::Abort
=> Err((span
, "abort is not stable in const fn".into())),
395 TerminatorKind
::GeneratorDrop
| TerminatorKind
::Yield { .. }
=> {
396 Err((span
, "const fn generators are unstable".into()))
399 TerminatorKind
::Call
{
407 let fn_ty
= func
.ty(body
, tcx
);
408 if let ty
::FnDef(fn_def_id
, _
) = fn_ty
.kind
{
409 // Allow unstable const if we opt in by using #[allow_internal_unstable]
410 // on function or macro declaration.
411 if !crate::const_eval
::is_min_const_fn(tcx
, fn_def_id
)
412 && !crate::const_eval
::is_unstable_const_fn(tcx
, fn_def_id
)
414 span
.allows_unstable(feature
)
415 || lib_feature_allowed(tcx
, def_id
, feature
)
422 "can only call other `const fn` within a `const fn`, \
423 but `{:?}` is not stable as `const fn`",
430 // HACK: This is to "unstabilize" the `transmute` intrinsic
431 // within const fns. `transmute` is allowed in all other const contexts.
432 // This won't really scale to more intrinsics or functions. Let's allow const
433 // transmutes in const fn before we add more hacks to this.
434 if tcx
.fn_sig(fn_def_id
).abi() == RustIntrinsic
435 && tcx
.item_name(fn_def_id
) == sym
::transmute
436 && !feature_allowed(tcx
, def_id
, sym
::const_fn_transmute
)
440 "can only call `transmute` from const items, not `const fn`".into(),
444 check_operand(tcx
, func
, span
, fn_def_id
, body
)?
;
447 check_operand(tcx
, arg
, span
, fn_def_id
, body
)?
;
451 Err((span
, "can only call other const fns within const fn".into()))
455 TerminatorKind
::Assert { cond, expected: _, msg: _, target: _, cleanup: _ }
=> {
456 check_operand(tcx
, cond
, span
, def_id
, body
)
459 TerminatorKind
::InlineAsm { .. }
=> {
460 Err((span
, "cannot use inline assembly in const fn".into()))