diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_const_eval/messages.ftl | 4 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/check_consts/check.rs | 89 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/check_consts/ops.rs | 53 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/errors.rs | 10 | ||||
| -rw-r--r-- | compiler/rustc_session/src/parse.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_session/src/session.rs | 1 |
6 files changed, 80 insertions, 78 deletions
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 826e34930ea..15027ae0c18 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -25,6 +25,10 @@ const_eval_closure_fndef_not_const = function defined here, but it is not `const` const_eval_closure_non_const = cannot call non-const closure in {const_eval_const_context}s + +const_eval_conditionally_const_call = + cannot call conditionally-const {$def_descr} `{$def_path_str}` in {const_eval_const_context}s + const_eval_consider_dereferencing = consider dereferencing here diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index aea3d5bd3e7..8cd0ecb3e4e 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -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) { - trace!("attempting to call a trait method"); + // 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. diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 2931159842f..036ca763280 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -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 }) } diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 14e8bebbb18..604e5ed61a3 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -177,6 +177,16 @@ pub(crate) struct NonConstFmtMacroCall { } #[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 { #[primary_span] diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index f20ae85b8e8..21c11655110 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -155,6 +155,7 @@ pub fn feature_warn_issue( } /// Adds the diagnostics for a feature to an existing error. +/// Must be a language feature! pub fn add_feature_diagnostics<G: EmissionGuarantee>( err: &mut Diag<'_, G>, sess: &Session, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 470e372ee48..9d9434a7776 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -300,6 +300,7 @@ impl Session { self.opts.test } + /// `feature` must be a language feature. #[track_caller] pub fn create_feature_err<'a>(&'a self, err: impl Diagnostic<'a>, feature: Symbol) -> Diag<'a> { let mut err = self.dcx().create_err(err); |
