use rustc::hir;
use rustc::mir::*;
use rustc::ty::{self, Predicate, Ty, TyCtxt, adjustment::{PointerCast}};
-use rustc_target::spec::abi;
use std::borrow::Cow;
use syntax_pos::Span;
use syntax::symbol::{sym, Symbol};
fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, fn_def_id: DefId) -> McfResult {
for ty in ty.walk() {
match ty.kind {
- ty::Ref(_, _, hir::Mutability::MutMutable) => return Err((
- span,
- "mutable references in const fn are unstable".into(),
- )),
+ ty::Ref(_, _, hir::Mutability::Mutable) => {
+ if !feature_allowed(tcx, fn_def_id, sym::const_mut_refs) {
+ return Err((
+ span,
+ "mutable references in const fn are unstable".into(),
+ ))
+ }
+ }
ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
ty::FnPtr(..) => {
if !tcx.const_fn_is_allowed_fn_ptr(fn_def_id) {
_ => check_operand(tcx, operand, span, def_id, body),
}
}
- Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer), operand, _) => {
+ Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer), operand, _)
+ | Rvalue::Cast(CastKind::Pointer(PointerCast::ArrayToPointer), operand, _) => {
check_operand(tcx, operand, span, def_id, body)
}
Rvalue::Cast(CastKind::Pointer(PointerCast::UnsafeFnPointer), _, _) |
check_rvalue(tcx, body, def_id, rval, span)
}
- StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => {
+ | StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _)
+ if !feature_allowed(tcx, def_id, sym::const_if_match)
+ => {
Err((span, "loops and conditional expressions are not stable in const fn".into()))
}
StatementKind::FakeRead(_, place) => check_place(tcx, place, span, def_id, body),
// just an assignment
- StatementKind::SetDiscriminant { .. } => Ok(()),
+ StatementKind::SetDiscriminant { place, .. } => check_place(tcx, place, span, def_id, body),
| StatementKind::InlineAsm { .. } => {
Err((span, "cannot use inline assembly in const fn".into()))
Operand::Move(place) | Operand::Copy(place) => {
check_place(tcx, place, span, def_id, body)
}
- Operand::Constant(_) => Ok(()),
+ Operand::Constant(c) => match c.check_static_ptr(tcx) {
+ Some(_) => Err((span, "cannot access `static` items in const fn".into())),
+ None => Ok(()),
+ },
}
}
while let &[ref proj_base @ .., elem] = cursor {
cursor = proj_base;
match elem {
- ProjectionElem::Downcast(..) => {
- return Err((span, "`match` or `if let` in `const fn` is unstable".into()));
- }
+ ProjectionElem::Downcast(..) if !feature_allowed(tcx, def_id, sym::const_if_match)
+ => return Err((span, "`match` or `if let` in `const fn` is unstable".into())),
+ ProjectionElem::Downcast(_symbol, _variant_index) => {}
+
ProjectionElem::Field(..) => {
let base_ty = Place::ty_from(&place.base, &proj_base, body, tcx).ty;
if let Some(def) = base_ty.ty_adt_def() {
}
}
- match place.base {
- PlaceBase::Static(box Static { kind: StaticKind::Static, .. }) => {
- Err((span, "cannot access `static` items in const fn".into()))
- }
- PlaceBase::Local(_)
- | PlaceBase::Static(box Static { kind: StaticKind::Promoted(_, _), .. }) => Ok(()),
- }
+ Ok(())
}
/// Returns whether `allow_internal_unstable(..., <feature_gate>, ...)` is present.
check_operand(tcx, value, span, def_id, body)
},
- TerminatorKind::FalseEdges { .. } | TerminatorKind::SwitchInt { .. } => Err((
+ | TerminatorKind::FalseEdges { .. }
+ | TerminatorKind::SwitchInt { .. }
+ if !feature_allowed(tcx, def_id, sym::const_if_match)
+ => Err((
span,
"loops and conditional expressions are not stable in const fn".into(),
)),
+
+ TerminatorKind::FalseEdges { .. } => Ok(()),
+ TerminatorKind::SwitchInt { discr, switch_ty: _, values: _, targets: _ } => {
+ check_operand(tcx, discr, span, def_id, body)
+ }
+
+ // FIXME(ecstaticmorse): We probably want to allow `Unreachable` unconditionally.
+ TerminatorKind::Unreachable if feature_allowed(tcx, def_id, sym::const_if_match) => Ok(()),
+
| TerminatorKind::Abort | TerminatorKind::Unreachable => {
Err((span, "const fn with unreachable code is not stable".into()))
}
} => {
let fn_ty = func.ty(body, tcx);
if let ty::FnDef(def_id, _) = fn_ty.kind {
-
- // some intrinsics are waved through if called inside the
- // standard library. Users never need to call them directly
- match tcx.fn_sig(def_id).abi() {
- abi::Abi::RustIntrinsic => if !is_intrinsic_whitelisted(tcx, def_id) {
- return Err((
- span,
- "can only call a curated list of intrinsics in `min_const_fn`".into(),
- ))
- },
- abi::Abi::Rust if tcx.is_min_const_fn(def_id) => {},
- abi::Abi::Rust => return Err((
+ if !tcx.is_min_const_fn(def_id) {
+ return Err((
span,
format!(
"can only call other `const fn` within a `const fn`, \
func,
)
.into(),
- )),
- abi => return Err((
- span,
- format!(
- "cannot call functions with `{}` abi in `min_const_fn`",
- abi,
- ).into(),
- )),
+ ));
}
check_operand(tcx, func, span, def_id, body)?;
cleanup: _,
} => check_operand(tcx, cond, span, def_id, body),
+ | TerminatorKind::FalseUnwind { .. }
+ if feature_allowed(tcx, def_id, sym::const_loop)
+ => Ok(()),
+
TerminatorKind::FalseUnwind { .. } => {
Err((span, "loops are not allowed in const fn".into()))
- },
- }
-}
-
-/// Returns `true` if the `def_id` refers to an intrisic which we've whitelisted
-/// for being called from stable `const fn`s (`min_const_fn`).
-///
-/// Adding more intrinsics requires sign-off from @rust-lang/lang.
-fn is_intrinsic_whitelisted(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
- match &tcx.item_name(def_id).as_str()[..] {
- | "size_of"
- | "min_align_of"
- | "needs_drop"
- // Arithmetic:
- | "add_with_overflow" // ~> .overflowing_add
- | "sub_with_overflow" // ~> .overflowing_sub
- | "mul_with_overflow" // ~> .overflowing_mul
- | "wrapping_add" // ~> .wrapping_add
- | "wrapping_sub" // ~> .wrapping_sub
- | "wrapping_mul" // ~> .wrapping_mul
- | "saturating_add" // ~> .saturating_add
- | "saturating_sub" // ~> .saturating_sub
- | "unchecked_shl" // ~> .wrapping_shl
- | "unchecked_shr" // ~> .wrapping_shr
- | "rotate_left" // ~> .rotate_left
- | "rotate_right" // ~> .rotate_right
- | "ctpop" // ~> .count_ones
- | "ctlz" // ~> .leading_zeros
- | "cttz" // ~> .trailing_zeros
- | "bswap" // ~> .swap_bytes
- | "bitreverse" // ~> .reverse_bits
- => true,
- _ => false,
+ }
}
}