tcx.is_const_fn doesn't work the way it is described, remove it

Then we can rename the _raw functions to drop their suffix, and instead
explicitly use is_stable_const_fn for the few cases where that is really what
you want.
This commit is contained in:
Ralf Jung 2024-10-12 20:37:35 +02:00
parent 36dda4571d
commit 8849ac6042
21 changed files with 55 additions and 64 deletions

View File

@ -731,7 +731,7 @@ fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location
} }
// Trait functions are not `const fn` so we have to skip them here. // Trait functions are not `const fn` so we have to skip them here.
if !tcx.is_const_fn_raw(callee) && !is_trait { if !tcx.is_const_fn(callee) && !is_trait {
self.check_op(ops::FnCallNonConst { self.check_op(ops::FnCallNonConst {
caller, caller,
callee, callee,

View File

@ -112,7 +112,7 @@ pub fn is_safe_to_expose_on_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> b
} }
// Const-stability is only relevant for `const fn`. // Const-stability is only relevant for `const fn`.
assert!(tcx.is_const_fn_raw(def_id)); assert!(tcx.is_const_fn(def_id));
match tcx.lookup_const_stability(def_id) { match tcx.lookup_const_stability(def_id) {
None => { None => {

View File

@ -122,7 +122,7 @@ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> {
if let Ok(Some(ImplSource::UserDefined(data))) = implsrc { if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
// FIXME(effects) revisit this // FIXME(effects) revisit this
if !tcx.is_const_trait_impl_raw(data.impl_def_id) { if !tcx.is_const_trait_impl(data.impl_def_id) {
let span = tcx.def_span(data.impl_def_id); let span = tcx.def_span(data.impl_def_id);
err.subdiagnostic(errors::NonConstImplNote { span }); err.subdiagnostic(errors::NonConstImplNote { span });
} }
@ -174,7 +174,7 @@ macro_rules! error {
let note = match self_ty.kind() { let note = match self_ty.kind() {
FnDef(def_id, ..) => { FnDef(def_id, ..) => {
let span = tcx.def_span(*def_id); let span = tcx.def_span(*def_id);
if ccx.tcx.is_const_fn_raw(*def_id) { if ccx.tcx.is_const_fn(*def_id) {
span_bug!(span, "calling const FnDef errored when it shouldn't"); span_bug!(span, "calling const FnDef errored when it shouldn't");
} }

View File

@ -431,8 +431,8 @@ fn find_mir_or_eval_fn(
// sensitive check here. But we can at least rule out functions that are not const at // sensitive check here. But we can at least rule out functions that are not const at
// all. That said, we have to allow calling functions inside a trait marked with // all. That said, we have to allow calling functions inside a trait marked with
// #[const_trait]. These *are* const-checked! // #[const_trait]. These *are* const-checked!
// FIXME: why does `is_const_fn_raw` not classify them as const? // FIXME(effects): why does `is_const_fn` not classify them as const?
if (!ecx.tcx.is_const_fn_raw(def) && !ecx.tcx.is_const_default_method(def)) if (!ecx.tcx.is_const_fn(def) && !ecx.tcx.is_const_default_method(def))
|| ecx.tcx.has_attr(def, sym::rustc_do_not_const_check) || ecx.tcx.has_attr(def, sym::rustc_do_not_const_check)
{ {
// We certainly do *not* want to actually call the fn // We certainly do *not* want to actually call the fn

View File

@ -1597,7 +1597,7 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTrai
impl_.of_trait.as_ref().map(|ast_trait_ref| { impl_.of_trait.as_ref().map(|ast_trait_ref| {
let selfty = tcx.type_of(def_id).instantiate_identity(); let selfty = tcx.type_of(def_id).instantiate_identity();
check_impl_constness(tcx, tcx.is_const_trait_impl_raw(def_id.to_def_id()), ast_trait_ref); check_impl_constness(tcx, tcx.is_const_trait_impl(def_id.to_def_id()), ast_trait_ref);
let trait_ref = icx.lowerer().lower_impl_trait_ref(ast_trait_ref, selfty); let trait_ref = icx.lowerer().lower_impl_trait_ref(ast_trait_ref, selfty);

View File

@ -537,7 +537,7 @@ fn confirm_builtin_call(
// //
// This check is here because there is currently no way to express a trait bound for `FnDef` types only. // This check is here because there is currently no way to express a trait bound for `FnDef` types only.
if let ty::FnDef(def_id, _args) = *arg_ty.kind() { if let ty::FnDef(def_id, _args) = *arg_ty.kind() {
if idx == 0 && !self.tcx.is_const_fn_raw(def_id) { if idx == 0 && !self.tcx.is_const_fn(def_id) {
self.dcx().emit_err(errors::ConstSelectMustBeConst { span }); self.dcx().emit_err(errors::ConstSelectMustBeConst { span });
} }
} else { } else {

View File

@ -1751,7 +1751,7 @@ fn enforce_repeat_element_needs_copy_bound(
// to tell them that in the diagnostic. Does not affect typeck. // to tell them that in the diagnostic. Does not affect typeck.
let is_constable = match element.kind { let is_constable = match element.kind {
hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() { hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
ty::FnDef(def_id, _) if tcx.is_const_fn(def_id) => traits::IsConstable::Fn, ty::FnDef(def_id, _) if tcx.is_stable_const_fn(def_id) => traits::IsConstable::Fn,
_ => traits::IsConstable::No, _ => traits::IsConstable::No,
}, },
hir::ExprKind::Path(qpath) => { hir::ExprKind::Path(qpath) => {

View File

@ -1081,7 +1081,7 @@ fn should_encode_mir(
&& (generics.requires_monomorphization(tcx) && (generics.requires_monomorphization(tcx)
|| tcx.cross_crate_inlinable(def_id))); || tcx.cross_crate_inlinable(def_id)));
// The function has a `const` modifier or is in a `#[const_trait]`. // The function has a `const` modifier or is in a `#[const_trait]`.
let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id()) let is_const_fn = tcx.is_const_fn(def_id.to_def_id())
|| tcx.is_const_default_method(def_id.to_def_id()); || tcx.is_const_default_method(def_id.to_def_id());
(is_const_fn, opt) (is_const_fn, opt)
} }

View File

@ -329,7 +329,7 @@ pub fn body_const_context(self, def_id: impl Into<DefId>) -> Option<ConstContext
BodyOwnerKind::Static(mutability) => ConstContext::Static(mutability), BodyOwnerKind::Static(mutability) => ConstContext::Static(mutability),
BodyOwnerKind::Fn if self.tcx.is_constructor(def_id) => return None, BodyOwnerKind::Fn if self.tcx.is_constructor(def_id) => return None,
BodyOwnerKind::Fn | BodyOwnerKind::Closure if self.tcx.is_const_fn_raw(def_id) => { BodyOwnerKind::Fn | BodyOwnerKind::Closure if self.tcx.is_const_fn(def_id) => {
ConstContext::ConstFn ConstContext::ConstFn
} }
BodyOwnerKind::Fn if self.tcx.is_const_default_method(def_id) => ConstContext::ConstFn, BodyOwnerKind::Fn if self.tcx.is_const_default_method(def_id) => ConstContext::ConstFn,

View File

@ -17,7 +17,7 @@ pub fn write_mir_graphviz<W>(tcx: TyCtxt<'_>, single: Option<DefId>, w: &mut W)
let mirs = def_ids let mirs = def_ids
.iter() .iter()
.flat_map(|def_id| { .flat_map(|def_id| {
if tcx.is_const_fn_raw(*def_id) { if tcx.is_const_fn(*def_id) {
vec![tcx.optimized_mir(*def_id), tcx.mir_for_ctfe(*def_id)] vec![tcx.optimized_mir(*def_id), tcx.mir_for_ctfe(*def_id)]
} else { } else {
vec![tcx.instance_mir(ty::InstanceKind::Item(*def_id))] vec![tcx.instance_mir(ty::InstanceKind::Item(*def_id))]

View File

@ -317,7 +317,7 @@ pub fn write_mir_pretty<'tcx>(
}; };
// For `const fn` we want to render both the optimized MIR and the MIR for ctfe. // For `const fn` we want to render both the optimized MIR and the MIR for ctfe.
if tcx.is_const_fn_raw(def_id) { if tcx.is_const_fn(def_id) {
render_body(w, tcx.optimized_mir(def_id))?; render_body(w, tcx.optimized_mir(def_id))?;
writeln!(w)?; writeln!(w)?;
writeln!(w, "// MIR FOR CTFE")?; writeln!(w, "// MIR FOR CTFE")?;

View File

@ -741,12 +741,11 @@
desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) } desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) }
} }
/// Returns `true` if this is a const fn, use the `is_const_fn` to know whether your crate /// Returns `true` if this is a const fn / const impl.
/// actually sees it as const fn (e.g., the const-fn-ness might be unstable and you might
/// not have the feature gate active).
/// ///
/// **Do not call this function manually.** It is only meant to cache the base data for the /// **Do not call this function manually.** It is only meant to cache the base data for the
/// `is_const_fn` function. Consider using `is_const_fn` or `is_const_fn_raw` instead. /// higher-level functions. Consider using `is_const_fn` or `is_const_trait_impl` instead.
/// Also note that neither of them takes into account feature gates and stability.
query constness(key: DefId) -> hir::Constness { query constness(key: DefId) -> hir::Constness {
desc { |tcx| "checking if item is const: `{}`", tcx.def_path_str(key) } desc { |tcx| "checking if item is const: `{}`", tcx.def_path_str(key) }
separate_provide_extern separate_provide_extern

View File

@ -3120,39 +3120,24 @@ pub fn map_opaque_lifetime_to_parent_lifetime(
} }
} }
/// Whether the `def_id` counts as const fn in the current crate, considering all active /// Whether `def_id` is a stable const fn (i.e., doesn't need any feature gates to be called).
/// feature gates ///
pub fn is_const_fn(self, def_id: DefId) -> bool { /// When this is `false`, the function may still be callable as a `const fn` due to features
if self.is_const_fn_raw(def_id) { /// being enabled!
match self.lookup_const_stability(def_id) { pub fn is_stable_const_fn(self, def_id: DefId) -> bool {
Some(stability) if stability.is_const_unstable() => { self.is_const_fn(def_id)
// has a `rustc_const_unstable` attribute, check whether the user enabled the && match self.lookup_const_stability(def_id) {
// corresponding feature gate. None => true, // a fn in a non-staged_api crate
stability.feature.is_some_and(|f| self.features().enabled(f)) Some(stability) if stability.is_const_stable() => true,
} _ => false,
// 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
_ => true,
} }
} else {
false
}
} }
// FIXME(effects): Please remove this. It's a footgun. // FIXME(effects): Please remove this. It's a footgun.
/// Whether the trait impl is marked const. This does not consider stability or feature gates. /// Whether the trait impl is marked const. This does not consider stability or feature gates.
pub fn is_const_trait_impl_raw(self, def_id: DefId) -> bool { pub fn is_const_trait_impl(self, def_id: DefId) -> bool {
let Some(local_def_id) = def_id.as_local() else { return false }; self.def_kind(def_id) == DefKind::Impl { of_trait: true }
let node = self.hir_node_by_def_id(local_def_id); && self.constness(def_id) == hir::Constness::Const
matches!(
node,
hir::Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { constness, .. }),
..
}) if matches!(constness, hir::Constness::Const)
)
} }
pub fn intrinsic(self, def_id: impl IntoQueryParam<DefId> + Copy) -> Option<ty::IntrinsicDef> { pub fn intrinsic(self, def_id: impl IntoQueryParam<DefId> + Copy) -> Option<ty::IntrinsicDef> {

View File

@ -1995,8 +1995,11 @@ pub fn adjust_ident_and_get_scope(
(ident, scope) (ident, scope)
} }
/// Checks whether this is a `const fn`. Returns `false` for non-functions.
///
/// Even if this returns `true`, constness may still be unstable!
#[inline] #[inline]
pub fn is_const_fn_raw(self, def_id: DefId) -> bool { pub fn is_const_fn(self, def_id: DefId) -> bool {
matches!( matches!(
self.def_kind(def_id), self.def_kind(def_id),
DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Closure DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Closure

View File

@ -673,7 +673,7 @@ fn validate_call(
} }
// Make sure the callee is a `const fn`. // Make sure the callee is a `const fn`.
let is_const_fn = match *fn_ty.kind() { let is_const_fn = match *fn_ty.kind() {
ty::FnDef(def_id, _) => self.tcx.is_const_fn_raw(def_id), ty::FnDef(def_id, _) => self.tcx.is_const_fn(def_id),
_ => false, _ => false,
}; };
if !is_const_fn { if !is_const_fn {

View File

@ -1997,7 +1997,7 @@ fn check_rustc_allow_const_fn_unstable(
) { ) {
match target { match target {
Target::Fn | Target::Method(_) Target::Fn | Target::Method(_)
if self.tcx.is_const_fn_raw(hir_id.expect_owner().to_def_id()) => {} if self.tcx.is_const_fn(hir_id.expect_owner().to_def_id()) => {}
// FIXME(#80564): We permit struct fields and match arms to have an // FIXME(#80564): We permit struct fields and match arms to have an
// `#[allow_internal_unstable]` attribute with just a lint, because we previously // `#[allow_internal_unstable]` attribute with just a lint, because we previously
// erroneously allowed it and some crates used it accidentally, to be compatible // erroneously allowed it and some crates used it accidentally, to be compatible

View File

@ -179,7 +179,7 @@ fn annotate<F>(
// their ABI; `fn_sig.abi` is *not* correct for foreign functions. // their ABI; `fn_sig.abi` is *not* correct for foreign functions.
&& !is_foreign_item && !is_foreign_item
&& const_stab.is_some() && const_stab.is_some()
&& (!self.in_trait_impl || !self.tcx.is_const_fn_raw(def_id.to_def_id())) && (!self.in_trait_impl || !self.tcx.is_const_fn(def_id.to_def_id()))
{ {
self.tcx.dcx().emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span }); self.tcx.dcx().emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span });
} }
@ -597,8 +597,8 @@ fn check_missing_or_wrong_const_stability(&self, def_id: LocalDefId, span: Span)
return; return;
} }
let is_const = self.tcx.is_const_fn_raw(def_id.to_def_id()) let is_const = self.tcx.is_const_fn(def_id.to_def_id())
|| self.tcx.is_const_trait_impl_raw(def_id.to_def_id()); || self.tcx.is_const_trait_impl(def_id.to_def_id());
let is_stable = let is_stable =
self.tcx.lookup_stability(def_id).is_some_and(|stability| stability.level.is_stable()); self.tcx.lookup_stability(def_id).is_some_and(|stability| stability.level.is_stable());
let missing_const_stability_attribute = let missing_const_stability_attribute =
@ -820,7 +820,7 @@ fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
// `#![feature(const_trait_impl)]` is unstable, so any impl declared stable // `#![feature(const_trait_impl)]` is unstable, so any impl declared stable
// needs to have an error emitted. // needs to have an error emitted.
if features.const_trait_impl() if features.const_trait_impl()
&& self.tcx.is_const_trait_impl_raw(item.owner_id.to_def_id()) && self.tcx.is_const_trait_impl(item.owner_id.to_def_id())
&& const_stab.is_some_and(|(stab, _)| stab.is_const_stable()) && const_stab.is_some_and(|(stab, _)| stab.is_const_stable())
{ {
self.tcx.dcx().emit_err(errors::TraitImplConstStable { span: item.span }); self.tcx.dcx().emit_err(errors::TraitImplConstStable { span: item.span });

View File

@ -393,7 +393,7 @@ fn assemble_closure_candidates(
let self_ty = obligation.self_ty().skip_binder(); let self_ty = obligation.self_ty().skip_binder();
match *self_ty.kind() { match *self_ty.kind() {
ty::Closure(def_id, _) => { ty::Closure(def_id, _) => {
let is_const = self.tcx().is_const_fn_raw(def_id); let is_const = self.tcx().is_const_fn(def_id);
debug!(?kind, ?obligation, "assemble_unboxed_candidates"); debug!(?kind, ?obligation, "assemble_unboxed_candidates");
match self.infcx.closure_kind(self_ty) { match self.infcx.closure_kind(self_ty) {
Some(closure_kind) => { Some(closure_kind) => {
@ -413,7 +413,7 @@ fn assemble_closure_candidates(
} }
ty::CoroutineClosure(def_id, args) => { ty::CoroutineClosure(def_id, args) => {
let args = args.as_coroutine_closure(); let args = args.as_coroutine_closure();
let is_const = self.tcx().is_const_fn_raw(def_id); let is_const = self.tcx().is_const_fn(def_id);
if let Some(closure_kind) = self.infcx.closure_kind(self_ty) if let Some(closure_kind) = self.infcx.closure_kind(self_ty)
// Ambiguity if upvars haven't been constrained yet // Ambiguity if upvars haven't been constrained yet
&& !args.tupled_upvars_ty().is_ty_var() && !args.tupled_upvars_ty().is_ty_var()

View File

@ -640,7 +640,7 @@ fn build_fn_header(
asyncness: ty::Asyncness, asyncness: ty::Asyncness,
) -> hir::FnHeader { ) -> hir::FnHeader {
let sig = tcx.fn_sig(def_id).skip_binder(); let sig = tcx.fn_sig(def_id).skip_binder();
let constness = if tcx.is_const_fn_raw(def_id) { let constness = if tcx.is_const_fn(def_id) {
hir::Constness::Const hir::Constness::Const
} else { } else {
hir::Constness::NotConst hir::Constness::NotConst
@ -662,7 +662,7 @@ fn build_fn_header(
safety safety
}, },
abi, abi,
constness: if tcx.is_const_fn_raw(def_id) { constness: if tcx.is_const_fn(def_id) {
hir::Constness::Const hir::Constness::Const
} else { } else {
hir::Constness::NotConst hir::Constness::NotConst

View File

@ -334,7 +334,7 @@ fn check_terminator<'tcx>(
| TerminatorKind::TailCall { func, args, fn_span: _ } => { | TerminatorKind::TailCall { func, args, fn_span: _ } => {
let fn_ty = func.ty(body, tcx); let fn_ty = func.ty(body, tcx);
if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() { if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
if !is_const_fn(tcx, fn_def_id, msrv) { if !is_stable_const_fn(tcx, fn_def_id, msrv) {
return Err(( return Err((
span, span,
format!( format!(
@ -377,12 +377,12 @@ fn check_terminator<'tcx>(
} }
} }
fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool { fn is_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
tcx.is_const_fn(def_id) tcx.is_const_fn(def_id)
&& tcx.lookup_const_stability(def_id).map_or(true, |const_stab| { && tcx.lookup_const_stability(def_id).is_none_or(|const_stab| {
if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level { if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level {
// Checking MSRV is manually necessary because `rustc` has no such concept. This entire // Checking MSRV is manually necessary because `rustc` has no such concept. This entire
// function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`. // function could be removed if `rustc` provided a MSRV-aware version of `is_stable_const_fn`.
// as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
let const_stab_rust_version = match since { let const_stab_rust_version = match since {
@ -393,8 +393,12 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
msrv.meets(const_stab_rust_version) msrv.meets(const_stab_rust_version)
} else { } else {
// Unstable const fn with the feature enabled. // Unstable const fn, check if the feature is enabled. We need both the regular stability
msrv.current().is_none() // feature and (if set) the const stability feature to const-call this function.
let stab = tcx.lookup_stability(def_id);
let is_enabled = stab.is_some_and(|s| s.is_stable() || tcx.features().enabled(s.feature))
&& const_stab.feature.is_none_or(|f| tcx.features().enabled(f));
is_enabled && msrv.current().is_none()
} }
}) })
} }

View File

@ -346,13 +346,13 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
.cx .cx
.qpath_res(p, hir_id) .qpath_res(p, hir_id)
.opt_def_id() .opt_def_id()
.map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {}, .map_or(false, |id| self.cx.tcx.is_const_fn(id)) => {},
ExprKind::MethodCall(..) ExprKind::MethodCall(..)
if self if self
.cx .cx
.typeck_results() .typeck_results()
.type_dependent_def_id(e.hir_id) .type_dependent_def_id(e.hir_id)
.map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {}, .map_or(false, |id| self.cx.tcx.is_const_fn(id)) => {},
ExprKind::Binary(_, lhs, rhs) ExprKind::Binary(_, lhs, rhs)
if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty() if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
&& self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {}, && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},