From 63d7db8dfdc08d99a12b9c4299e73d673890d401 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 8 Nov 2024 19:18:44 +0000 Subject: [PATCH] Disable calling all const traits in stable const --- .../src/check_consts/check.rs | 68 +++------ .../rustc_const_eval/src/check_consts/ops.rs | 11 ++ tests/ui/traits/const-traits/staged-api.rs | 20 +-- .../const-traits/staged-api.stable.stderr | 131 ++++++++++++++---- .../const-traits/staged-api.unstable.stderr | 59 ++++---- 5 files changed, 163 insertions(+), 126 deletions(-) diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index aea3d5bd3e70f..fa1d72c51e911 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; @@ -627,11 +627,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(..) => { @@ -647,55 +647,25 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { self.revalidate_conditional_constness(callee, fn_args, call_source, *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"); - 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; - } - } 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 - }; - 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; - } + // We are not allowed to use const traits in stable const functions yet. + // FIXME: apply more fine-grained per-trait const stability checks + // (see ). + self.check_op(ops::FnCallNonConst { + callee, + args: fn_args, + span: *fn_span, + call_source, + feature: tcx.is_const_trait(trait_did).then_some(sym::const_trait_impl), + }); + + // We don't know which function is being called here, so we can't run the checks below. + // FIXME: apply more fine-grained per-trait const stability checks + // (see ). + return; } // At this point, we are calling a function, `callee`, whose `DefId` is known... @@ -784,7 +754,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } // 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, diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 2931159842f95..1fc32464f8984 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -81,6 +81,17 @@ pub(crate) struct FnCallNonConst<'tcx> { } impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { + fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { + match self.feature { + Some(feature) => Status::Unstable { + gate: feature, + safe_to_expose_on_stable: false, + is_function_call: false, + }, + None => Status::Forbidden, + } + } + // FIXME: make this translatable #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] diff --git a/tests/ui/traits/const-traits/staged-api.rs b/tests/ui/traits/const-traits/staged-api.rs index 401a81d81425e..59d199d0f3d26 100644 --- a/tests/ui/traits/const-traits/staged-api.rs +++ b/tests/ui/traits/const-traits/staged-api.rs @@ -1,3 +1,7 @@ +//@ known-bug: project-const-traits#5 +// staged const APIs are intentionally broken pending us deciding how to handle +// recursive const stability checks. + //@ revisions: stable unstable //@ compile-flags: -Znext-solver @@ -19,7 +23,6 @@ pub struct Foo; #[cfg_attr(unstable, rustc_const_unstable(feature = "local_feature", issue = "none"))] #[cfg_attr(stable, rustc_const_stable(feature = "local_feature", since = "1.0.0"))] impl const MyTrait for Foo { - //[stable]~^ ERROR trait implementations cannot be const stable yet fn func() {} } @@ -32,28 +35,18 @@ fn non_const_context() { #[unstable(feature = "none", issue = "none")] const fn const_context() { Unstable::func(); - //[unstable]~^ ERROR cannot use `#[feature(unstable)]` - //[stable]~^^ ERROR not yet stable as a const fn Foo::func(); - //[unstable]~^ ERROR cannot use `#[feature(local_feature)]` - //[stable]~^^ cannot be (indirectly) exposed to stable - // We get the error on `stable` since this is a trait function. Unstable2::func(); - //~^ ERROR not yet stable as a const fn // ^ fails, because the `unstable2` feature is not active } #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(unstable, rustc_const_unstable(feature = "local_feature", issue = "none"))] pub const fn const_context_not_const_stable() { - //[stable]~^ ERROR function has missing const stability attribute Unstable::func(); - //[stable]~^ ERROR not yet stable as a const fn Foo::func(); - //[stable]~^ cannot be (indirectly) exposed to stable // We get the error on `stable` since this is a trait function. Unstable2::func(); - //~^ ERROR not yet stable as a const fn // ^ fails, because the `unstable2` feature is not active } @@ -61,14 +54,9 @@ pub const fn const_context_not_const_stable() { #[rustc_const_stable(feature = "cheese", since = "1.0.0")] const fn stable_const_context() { Unstable::func(); - //[unstable]~^ ERROR cannot use `#[feature(unstable)]` - //[stable]~^^ ERROR not yet stable as a const fn Foo::func(); - //[unstable]~^ ERROR cannot use `#[feature(local_feature)]` - //[stable]~^^ cannot be (indirectly) exposed to stable // We get the error on `stable` since this is a trait function. const_context_not_const_stable() - //[unstable]~^ ERROR cannot use `#[feature(local_feature)]` } fn main() {} diff --git a/tests/ui/traits/const-traits/staged-api.stable.stderr b/tests/ui/traits/const-traits/staged-api.stable.stderr index 8f491b2f18238..321975ade1352 100644 --- a/tests/ui/traits/const-traits/staged-api.stable.stderr +++ b/tests/ui/traits/const-traits/staged-api.stable.stderr @@ -1,8 +1,7 @@ error: trait implementations cannot be const stable yet - --> $DIR/staged-api.rs:21:1 + --> $DIR/staged-api.rs:25:1 | LL | / impl const MyTrait for Foo { -LL | | LL | | fn func() {} LL | | } | |_^ @@ -10,80 +9,152 @@ LL | | } = note: see issue #67792 for more information error: function has missing const stability attribute - --> $DIR/staged-api.rs:48:1 + --> $DIR/staged-api.rs:45:1 | LL | / pub const fn const_context_not_const_stable() { -LL | | LL | | Unstable::func(); -LL | | -... | +LL | | Foo::func(); +LL | | // We get the error on `stable` since this is a trait function. +LL | | Unstable2::func(); LL | | // ^ fails, because the `unstable2` feature is not active LL | | } | |_^ -error: `::func` is not yet stable as a const fn - --> $DIR/staged-api.rs:34:5 +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:37:5 | LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | - = help: add `#![feature(unstable)]` to the crate attributes to enable +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | const fn const_context() { + | -error: `::func` cannot be (indirectly) exposed to stable - --> $DIR/staged-api.rs:37:5 +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:38:5 | LL | Foo::func(); | ^^^^^^^^^^^ | - = help: either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]` +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | const fn const_context() { + | -error: `::func` is not yet stable as a const fn - --> $DIR/staged-api.rs:41:5 +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:39:5 | LL | Unstable2::func(); | ^^^^^^^^^^^^^^^^^ | - = help: add `#![feature(unstable2)]` to the crate attributes to enable +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | const fn const_context() { + | -error: `::func` is not yet stable as a const fn - --> $DIR/staged-api.rs:50:5 +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:46:5 | LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | - = help: add `#![feature(unstable)]` to the crate attributes to enable +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | pub const fn const_context_not_const_stable() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | pub const fn const_context_not_const_stable() { + | -error: `::func` cannot be (indirectly) exposed to stable - --> $DIR/staged-api.rs:52:5 +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:47:5 | LL | Foo::func(); | ^^^^^^^^^^^ | - = help: either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]` +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | pub const fn const_context_not_const_stable() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | pub const fn const_context_not_const_stable() { + | -error: `::func` is not yet stable as a const fn - --> $DIR/staged-api.rs:55:5 +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:49:5 | LL | Unstable2::func(); | ^^^^^^^^^^^^^^^^^ | - = help: add `#![feature(unstable2)]` to the crate attributes to enable +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | pub const fn const_context_not_const_stable() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | pub const fn const_context_not_const_stable() { + | -error: `::func` is not yet stable as a const fn - --> $DIR/staged-api.rs:63:5 +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:56:5 | LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | - = help: add `#![feature(unstable)]` to the crate attributes to enable +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | const fn stable_const_context() { + | -error: `::func` cannot be (indirectly) exposed to stable - --> $DIR/staged-api.rs:66:5 +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:57:5 | LL | Foo::func(); | ^^^^^^^^^^^ | - = help: either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]` +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | const fn stable_const_context() { + | error: aborting due to 10 previous errors diff --git a/tests/ui/traits/const-traits/staged-api.unstable.stderr b/tests/ui/traits/const-traits/staged-api.unstable.stderr index 76275452e90c4..79a6d6feab0e7 100644 --- a/tests/ui/traits/const-traits/staged-api.unstable.stderr +++ b/tests/ui/traits/const-traits/staged-api.unstable.stderr @@ -1,93 +1,90 @@ -error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` - --> $DIR/staged-api.rs:34:5 +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:37:5 | LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] LL | const fn const_context() { | help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) | -LL + #[rustc_allow_const_fn_unstable(unstable)] +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | -error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local_feature)]` - --> $DIR/staged-api.rs:37:5 +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:38:5 | LL | Foo::func(); | ^^^^^^^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] LL | const fn const_context() { | help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) | -LL + #[rustc_allow_const_fn_unstable(local_feature)] +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | -error: `::func` is not yet stable as a const fn - --> $DIR/staged-api.rs:41:5 +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:39:5 | LL | Unstable2::func(); | ^^^^^^^^^^^^^^^^^ | - = help: add `#![feature(unstable2)]` to the crate attributes to enable - -error: `::func` is not yet stable as a const fn - --> $DIR/staged-api.rs:55:5 +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | -LL | Unstable2::func(); - | ^^^^^^^^^^^^^^^^^ +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | const fn const_context() { | - = help: add `#![feature(unstable2)]` to the crate attributes to enable -error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` - --> $DIR/staged-api.rs:63:5 +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:56:5 | LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] LL | const fn stable_const_context() { | help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) | -LL + #[rustc_allow_const_fn_unstable(unstable)] +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | -error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local_feature)]` - --> $DIR/staged-api.rs:66:5 +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:57:5 | LL | Foo::func(); | ^^^^^^^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features -help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] LL | const fn stable_const_context() { | help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) | -LL + #[rustc_allow_const_fn_unstable(local_feature)] +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local_feature)]` - --> $DIR/staged-api.rs:70:5 + --> $DIR/staged-api.rs:59:5 | LL | const_context_not_const_stable() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,5 +101,5 @@ LL + #[rustc_allow_const_fn_unstable(local_feature)] LL | const fn stable_const_context() { | -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors