Rollup merge of #132823 - RalfJung:conditional-const-calls, r=fee1-dead,compiler-errors
require const_impl_trait gate for all conditional and trait const calls Alternative to https://github.com/rust-lang/rust/pull/132786. `@compiler-errors` this is basically what I meant with my proposals. I found it's easier to express this in code than English. ;) r? `@compiler-errors`
This commit is contained in:
commit
d4c81c6987
15 changed files with 271 additions and 394 deletions
|
@ -15,7 +15,7 @@ use rustc_middle::mir::visit::Visitor;
|
|||
use rustc_middle::mir::*;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::adjustment::PointerCoercion;
|
||||
use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TypeVisitableExt};
|
||||
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
|
||||
use rustc_mir_dataflow::Analysis;
|
||||
use rustc_mir_dataflow::impls::MaybeStorageLive;
|
||||
use rustc_mir_dataflow::storage::always_storage_live_locals;
|
||||
|
@ -361,31 +361,21 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
|
|||
!is_transient
|
||||
}
|
||||
|
||||
/// Returns whether there are const-conditions.
|
||||
fn revalidate_conditional_constness(
|
||||
&mut self,
|
||||
callee: DefId,
|
||||
callee_args: ty::GenericArgsRef<'tcx>,
|
||||
call_source: CallSource,
|
||||
call_span: Span,
|
||||
) {
|
||||
) -> bool {
|
||||
let tcx = self.tcx;
|
||||
if !tcx.is_conditionally_const(callee) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
let const_conditions = tcx.const_conditions(callee).instantiate(tcx, callee_args);
|
||||
// If there are any const conditions on this fn and `const_trait_impl`
|
||||
// is not enabled, simply bail. We shouldn't be able to call conditionally
|
||||
// const functions on stable.
|
||||
if !const_conditions.is_empty() && !tcx.features().const_trait_impl() {
|
||||
self.check_op(ops::FnCallNonConst {
|
||||
callee,
|
||||
args: callee_args,
|
||||
span: call_span,
|
||||
call_source,
|
||||
feature: Some(sym::const_trait_impl),
|
||||
});
|
||||
return;
|
||||
if const_conditions.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let infcx = tcx.infer_ctxt().build(self.body.typing_mode(tcx));
|
||||
|
@ -421,6 +411,8 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
|
|||
tcx.dcx()
|
||||
.span_delayed_bug(call_span, "this should have reported a ~const error in HIR");
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -627,11 +619,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let ConstCx { tcx, body, param_env, .. } = *self.ccx;
|
||||
let ConstCx { tcx, body, .. } = *self.ccx;
|
||||
|
||||
let fn_ty = func.ty(body, tcx);
|
||||
|
||||
let (mut callee, mut fn_args) = match *fn_ty.kind() {
|
||||
let (callee, fn_args) = match *fn_ty.kind() {
|
||||
ty::FnDef(def_id, fn_args) => (def_id, fn_args),
|
||||
|
||||
ty::FnPtr(..) => {
|
||||
|
@ -645,57 +637,38 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
}
|
||||
};
|
||||
|
||||
self.revalidate_conditional_constness(callee, fn_args, call_source, *fn_span);
|
||||
let has_const_conditions =
|
||||
self.revalidate_conditional_constness(callee, fn_args, *fn_span);
|
||||
|
||||
let mut is_trait = false;
|
||||
// Attempting to call a trait method?
|
||||
if let Some(trait_did) = tcx.trait_of_item(callee) {
|
||||
// We can't determine the actual callee here, so we have to do different checks
|
||||
// than usual.
|
||||
|
||||
trace!("attempting to call a trait method");
|
||||
|
||||
let trait_is_const = tcx.is_const_trait(trait_did);
|
||||
// trait method calls are only permitted when `effects` is enabled.
|
||||
// typeck ensures the conditions for calling a const trait method are met,
|
||||
// so we only error if the trait isn't const. We try to resolve the trait
|
||||
// into the concrete method, and uses that for const stability checks.
|
||||
// FIXME(const_trait_impl) we might consider moving const stability checks
|
||||
// to typeck as well.
|
||||
if tcx.features().const_trait_impl() && trait_is_const {
|
||||
// This skips the check below that ensures we only call `const fn`.
|
||||
is_trait = true;
|
||||
|
||||
if let Ok(Some(instance)) =
|
||||
Instance::try_resolve(tcx, param_env, callee, fn_args)
|
||||
&& let InstanceKind::Item(def) = instance.def
|
||||
{
|
||||
// Resolve a trait method call to its concrete implementation, which may be in a
|
||||
// `const` trait impl. This is only used for the const stability check below, since
|
||||
// we want to look at the concrete impl's stability.
|
||||
fn_args = instance.args;
|
||||
callee = def;
|
||||
}
|
||||
if trait_is_const {
|
||||
// Trait calls are always conditionally-const.
|
||||
self.check_op(ops::ConditionallyConstCall { callee, args: fn_args });
|
||||
// FIXME(const_trait_impl): do a more fine-grained check whether this
|
||||
// particular trait can be const-stably called.
|
||||
} else {
|
||||
// if the trait is const but the user has not enabled the feature(s),
|
||||
// suggest them.
|
||||
let feature = if trait_is_const {
|
||||
Some(if tcx.features().const_trait_impl() {
|
||||
sym::effects
|
||||
} else {
|
||||
sym::const_trait_impl
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// Not even a const trait.
|
||||
self.check_op(ops::FnCallNonConst {
|
||||
callee,
|
||||
args: fn_args,
|
||||
span: *fn_span,
|
||||
call_source,
|
||||
feature,
|
||||
});
|
||||
// If we allowed this, we're in miri-unleashed mode, so we might
|
||||
// as well skip the remaining checks.
|
||||
return;
|
||||
}
|
||||
// That's all we can check here.
|
||||
return;
|
||||
}
|
||||
|
||||
// Even if we know the callee, ensure we can use conditionally-const calls.
|
||||
if has_const_conditions {
|
||||
self.check_op(ops::ConditionallyConstCall { callee, args: fn_args });
|
||||
}
|
||||
|
||||
// At this point, we are calling a function, `callee`, whose `DefId` is known...
|
||||
|
@ -783,14 +756,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
// Trait functions are not `const fn` so we have to skip them here.
|
||||
if !tcx.is_const_fn(callee) && !is_trait {
|
||||
if !tcx.is_const_fn(callee) {
|
||||
self.check_op(ops::FnCallNonConst {
|
||||
callee,
|
||||
args: fn_args,
|
||||
span: *fn_span,
|
||||
call_source,
|
||||
feature: None,
|
||||
});
|
||||
// If we allowed this, we're in miri-unleashed mode, so we might
|
||||
// as well skip the remaining checks.
|
||||
|
|
|
@ -70,6 +70,37 @@ impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
|
|||
}
|
||||
}
|
||||
|
||||
/// A call to a function that is in a trait, or has trait bounds that make it conditionally-const.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ConditionallyConstCall<'tcx> {
|
||||
pub callee: DefId,
|
||||
pub args: GenericArgsRef<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> NonConstOp<'tcx> for ConditionallyConstCall<'tcx> {
|
||||
fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
|
||||
// We use the `const_trait_impl` gate for all conditionally-const calls.
|
||||
Status::Unstable {
|
||||
gate: sym::const_trait_impl,
|
||||
safe_to_expose_on_stable: false,
|
||||
// We don't want the "mark the callee as `#[rustc_const_stable_indirect]`" hint
|
||||
is_function_call: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
|
||||
ccx.tcx.sess.create_feature_err(
|
||||
errors::ConditionallyConstCall {
|
||||
span,
|
||||
def_path_str: ccx.tcx.def_path_str_with_args(self.callee, self.args),
|
||||
def_descr: ccx.tcx.def_descr(self.callee),
|
||||
kind: ccx.const_kind(),
|
||||
},
|
||||
sym::const_trait_impl,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A function call where the callee is not marked as `const`.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) struct FnCallNonConst<'tcx> {
|
||||
|
@ -77,7 +108,6 @@ pub(crate) struct FnCallNonConst<'tcx> {
|
|||
pub args: GenericArgsRef<'tcx>,
|
||||
pub span: Span,
|
||||
pub call_source: CallSource,
|
||||
pub feature: Option<Symbol>,
|
||||
}
|
||||
|
||||
impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
|
||||
|
@ -85,7 +115,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
|
|||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> {
|
||||
let FnCallNonConst { callee, args, span, call_source, feature } = *self;
|
||||
let FnCallNonConst { callee, args, span, call_source } = *self;
|
||||
let ConstCx { tcx, param_env, .. } = *ccx;
|
||||
let caller = ccx.def_id();
|
||||
|
||||
|
@ -285,14 +315,6 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
|
|||
ccx.const_kind(),
|
||||
));
|
||||
|
||||
if let Some(feature) = feature {
|
||||
ccx.tcx.disabled_nightly_features(
|
||||
&mut err,
|
||||
Some(ccx.tcx.local_def_id_to_hir_id(caller)),
|
||||
[(String::new(), feature)],
|
||||
);
|
||||
}
|
||||
|
||||
if let ConstContext::Static(_) = ccx.const_kind() {
|
||||
err.note(fluent_generated::const_eval_lazy_lock);
|
||||
}
|
||||
|
@ -398,15 +420,8 @@ impl<'tcx> NonConstOp<'tcx> for Coroutine {
|
|||
|
||||
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
|
||||
let msg = format!("{:#}s are not allowed in {}s", self.0, ccx.const_kind());
|
||||
if let hir::CoroutineKind::Desugared(
|
||||
hir::CoroutineDesugaring::Async,
|
||||
hir::CoroutineSource::Block,
|
||||
) = self.0
|
||||
{
|
||||
ccx.tcx.sess.create_feature_err(
|
||||
errors::UnallowedOpInConstContext { span, msg },
|
||||
sym::const_async_blocks,
|
||||
)
|
||||
if let Status::Unstable { gate, .. } = self.status_in_item(ccx) {
|
||||
ccx.tcx.sess.create_feature_err(errors::UnallowedOpInConstContext { span, msg }, gate)
|
||||
} else {
|
||||
ccx.dcx().create_err(errors::UnallowedOpInConstContext { span, msg })
|
||||
}
|
||||
|
|
|
@ -176,6 +176,16 @@ pub(crate) struct NonConstFmtMacroCall {
|
|||
pub kind: ConstContext,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_conditionally_const_call)]
|
||||
pub(crate) struct ConditionallyConstCall {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub def_path_str: String,
|
||||
pub def_descr: &'static str,
|
||||
pub kind: ConstContext,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_non_const_fn_call, code = E0015)]
|
||||
pub(crate) struct NonConstFnCall {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue