Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move is_min_const_fn query to librustc_mir. #67781

Merged
merged 2 commits into from
Jan 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 0 additions & 156 deletions src/librustc/ty/constness.rs

This file was deleted.

2 changes: 0 additions & 2 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ pub mod cast;
#[macro_use]
pub mod codec;
pub mod _match;
mod constness;
mod erase_regions;
pub mod error;
pub mod fast_reject;
Expand Down Expand Up @@ -3318,7 +3317,6 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
context::provide(providers);
erase_regions::provide(providers);
layout::provide(providers);
constness::provide(providers);
*providers = ty::query::Providers {
asyncness,
associated_item,
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_mir/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ use crate::interpret::{intern_const_alloc_recursive, ConstValue, InterpCx};

mod error;
mod eval_queries;
mod fn_queries;
mod machine;

pub use error::*;
pub use eval_queries::*;
pub use fn_queries::*;
pub use machine::*;

/// Extracts a field of a (variant of a) const.
Expand Down
151 changes: 151 additions & 0 deletions src/librustc_mir/const_eval/fn_queries.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
use rustc::hir::map::blocks::FnLikeNode;
use rustc::ty::query::Providers;
use rustc::ty::TyCtxt;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_span::symbol::Symbol;
use rustc_target::spec::abi::Abi;
use syntax::attr;

/// Whether the `def_id` counts as const fn in your current crate, considering all active
/// feature gates
pub fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
tcx.is_const_fn_raw(def_id)
&& match is_unstable_const_fn(tcx, def_id) {
Some(feature_name) => {
// has a `rustc_const_unstable` attribute, check whether the user enabled the
// corresponding feature gate.
tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == feature_name)
}
// functions without const stability are either stable user written
// const fn or the user is using feature gates and we thus don't
// care what they do
None => true,
}
}

/// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it
pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Symbol> {
if tcx.is_const_fn_raw(def_id) {
let const_stab = tcx.lookup_const_stability(def_id)?;
if const_stab.level.is_unstable() { Some(const_stab.feature) } else { None }
} else {
None
}
}

/// Returns `true` if this function must conform to `min_const_fn`
pub fn is_min_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
// Bail out if the signature doesn't contain `const`
if !tcx.is_const_fn_raw(def_id) {
return false;
}

if tcx.features().staged_api {
// In order for a libstd function to be considered min_const_fn
// it needs to be stable and have no `rustc_const_unstable` attribute.
match tcx.lookup_const_stability(def_id) {
// `rustc_const_unstable` functions don't need to conform.
Some(&attr::ConstStability { ref level, .. }) if level.is_unstable() => false,
None => {
if let Some(stab) = tcx.lookup_stability(def_id) {
if stab.level.is_stable() {
tcx.sess.span_err(
tcx.def_span(def_id),
"stable const functions must have either `rustc_const_stable` or \
`rustc_const_unstable` attribute",
);
// While we errored above, because we don't know if we need to conform, we
// err on the "safe" side and require min_const_fn.
true
} else {
// Unstable functions need not conform to min_const_fn.
false
}
} else {
// Internal functions are forced to conform to min_const_fn.
// Annotate the internal function with a const stability attribute if
// you need to use unstable features.
// Note: this is an arbitrary choice that does not affect stability or const
// safety or anything, it just changes whether we need to annotate some
// internal functions with `rustc_const_stable` or with `rustc_const_unstable`
true
}
}
// Everything else needs to conform, because it would be callable from
// other `min_const_fn` functions.
_ => true,
}
} else {
// users enabling the `const_fn` feature gate can do what they want
!tcx.features().const_fn
}
}

pub fn provide(providers: &mut Providers<'_>) {
/// Const evaluability whitelist is here to check evaluability at the
/// top level beforehand.
fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
match tcx.fn_sig(def_id).abi() {
Abi::RustIntrinsic | Abi::PlatformIntrinsic => {
Some(tcx.lookup_const_stability(def_id).is_some())
}
_ => None,
}
}

/// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
/// said intrinsic is on the whitelist for being const callable.
fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
let hir_id = tcx
.hir()
.as_local_hir_id(def_id)
.expect("Non-local call to local provider is_const_fn");

let node = tcx.hir().get(hir_id);

if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
whitelisted
} else if let Some(fn_like) = FnLikeNode::from_node(node) {
fn_like.constness() == hir::Constness::Const
} else if let hir::Node::Ctor(_) = node {
true
} else {
false
}
}

fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
is_const_fn(tcx, def_id)
&& match tcx.lookup_const_stability(def_id) {
Some(stab) => {
if cfg!(debug_assertions) && stab.promotable {
let sig = tcx.fn_sig(def_id);
assert_eq!(
sig.unsafety(),
hir::Unsafety::Normal,
"don't mark const unsafe fns as promotable",
// https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
);
}
stab.promotable
}
None => false,
}
}

fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
is_const_fn(tcx, def_id)
&& tcx
.lookup_const_stability(def_id)
.map(|stab| stab.allow_const_fn_ptr)
.unwrap_or(false)
}

*providers = Providers {
is_const_fn_raw,
is_promotable_const_fn,
const_fn_is_allowed_fn_ptr,
..*providers
};
}
1 change: 1 addition & 0 deletions src/librustc_mir/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ use rustc::ty::query::Providers;

pub fn provide(providers: &mut Providers<'_>) {
borrow_check::provide(providers);
const_eval::provide(providers);
shim::provide(providers);
transform::provide(providers);
monomorphize::partitioning::provide(providers);
Expand Down
7 changes: 4 additions & 3 deletions src/librustc_mir/transform/check_consts/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use super::ops::{self, NonConstOp};
use super::qualifs::{self, HasMutInterior, NeedsDrop};
use super::resolver::FlowSensitiveAnalysis;
use super::{is_lang_panic_fn, ConstKind, Item, Qualif};
use crate::const_eval::{is_const_fn, is_unstable_const_fn};
use crate::dataflow::{self as old_dataflow, generic as dataflow};

pub type IndirectlyMutableResults<'mir, 'tcx> =
Expand Down Expand Up @@ -172,7 +173,7 @@ impl Validator<'a, 'mir, 'tcx> {
let Item { tcx, body, def_id, const_kind, .. } = *self.item;

let use_min_const_fn_checks = (const_kind == Some(ConstKind::ConstFn)
&& tcx.is_min_const_fn(def_id))
&& crate::const_eval::is_min_const_fn(tcx, def_id))
&& !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;

if use_min_const_fn_checks {
Expand Down Expand Up @@ -559,13 +560,13 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
};

// At this point, we are calling a function whose `DefId` is known...
if self.tcx.is_const_fn(def_id) {
if is_const_fn(self.tcx, def_id) {
return;
}

if is_lang_panic_fn(self.tcx, def_id) {
self.check_op(ops::Panic);
} else if let Some(feature) = self.tcx.is_unstable_const_fn(def_id) {
} else if let Some(feature) = is_unstable_const_fn(self.tcx, def_id) {
// Exempt unstable const fns inside of macros with
// `#[allow_internal_unstable]`.
if !self.span.allows_unstable(feature) {
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_mir/transform/check_unsafety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use rustc_span::symbol::{sym, Symbol};

use std::ops::Bound;

use crate::const_eval::{is_const_fn, is_min_const_fn};
use crate::util;

use rustc_error_codes::*;
Expand Down Expand Up @@ -522,7 +523,7 @@ fn unsafety_check_result(tcx: TyCtxt<'_>, def_id: DefId) -> UnsafetyCheckResult
let id = tcx.hir().as_local_hir_id(def_id).unwrap();
let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) {
hir::BodyOwnerKind::Closure => (false, false),
hir::BodyOwnerKind::Fn => (tcx.is_const_fn(def_id), tcx.is_min_const_fn(def_id)),
hir::BodyOwnerKind::Fn => (is_const_fn(tcx, def_id), is_min_const_fn(tcx, def_id)),
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false),
};
let mut checker = UnsafetyChecker::new(const_context, min_const_fn, body, tcx, param_env);
Expand Down
Loading