[clang] [clang][bytecode] Don't diagnose defined functions that will have a body (PR #165002)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 27 02:21:46 PDT 2025
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/165002
>From 11b3be1e1ad30eff085ebffdc39b57406d625148 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Fri, 24 Oct 2025 17:38:16 +0200
Subject: [PATCH] [clang][bytecode] Don't diagnose defined functions that will
have a body
But don't have one, yet. That happens for class methods, which are
"defined" but have no body, hence they willHaveBody.
Fixes https://github.com/llvm/llvm-project/issues/164995
---
clang/lib/AST/ByteCode/Interp.cpp | 110 +++++++++++++++-------------
clang/test/AST/ByteCode/records.cpp | 21 ++++++
2 files changed, 79 insertions(+), 52 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 169a9a2c7b811..0023a2d1eed7f 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -930,6 +930,15 @@ static bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
if (F->isValid() && F->hasBody() && F->isConstexpr())
return true;
+ const FunctionDecl *DiagDecl = F->getDecl();
+ const FunctionDecl *Definition = nullptr;
+ DiagDecl->getBody(Definition);
+
+ if (!Definition && S.checkingPotentialConstantExpression() &&
+ DiagDecl->isConstexpr()) {
+ return false;
+ }
+
// Implicitly constexpr.
if (F->isLambdaStaticInvoker())
return true;
@@ -937,7 +946,7 @@ static bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
// Bail out if the function declaration itself is invalid. We will
// have produced a relevant diagnostic while parsing it, so just
// note the problematic sub-expression.
- if (F->getDecl()->isInvalidDecl())
+ if (DiagDecl->isInvalidDecl())
return Invalid(S, OpPC);
// Diagnose failed assertions specially.
@@ -955,64 +964,61 @@ static bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
}
}
- if (S.getLangOpts().CPlusPlus11) {
- const FunctionDecl *DiagDecl = F->getDecl();
-
- // Invalid decls have been diagnosed before.
- if (DiagDecl->isInvalidDecl())
- return false;
+ if (!S.getLangOpts().CPlusPlus11) {
+ S.FFDiag(S.Current->getLocation(OpPC),
+ diag::note_invalid_subexpr_in_const_expr);
+ return false;
+ }
- // If this function is not constexpr because it is an inherited
- // non-constexpr constructor, diagnose that directly.
- const auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl);
- if (CD && CD->isInheritingConstructor()) {
- const auto *Inherited = CD->getInheritedConstructor().getConstructor();
- if (!Inherited->isConstexpr())
- DiagDecl = CD = Inherited;
- }
+ // Invalid decls have been diagnosed before.
+ if (DiagDecl->isInvalidDecl())
+ return false;
- // Silently reject constructors of invalid classes. The invalid class
- // has been rejected elsewhere before.
- if (CD && CD->getParent()->isInvalidDecl())
- return false;
+ // If this function is not constexpr because it is an inherited
+ // non-constexpr constructor, diagnose that directly.
+ const auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl);
+ if (CD && CD->isInheritingConstructor()) {
+ const auto *Inherited = CD->getInheritedConstructor().getConstructor();
+ if (!Inherited->isConstexpr())
+ DiagDecl = CD = Inherited;
+ }
- // FIXME: If DiagDecl is an implicitly-declared special member function
- // or an inheriting constructor, we should be much more explicit about why
- // it's not constexpr.
- if (CD && CD->isInheritingConstructor()) {
- S.FFDiag(S.Current->getLocation(OpPC),
- diag::note_constexpr_invalid_inhctor, 1)
- << CD->getInheritedConstructor().getConstructor()->getParent();
- S.Note(DiagDecl->getLocation(), diag::note_declared_at);
- } else {
- // Don't emit anything if the function isn't defined and we're checking
- // for a constant expression. It might be defined at the point we're
- // actually calling it.
- bool IsExtern = DiagDecl->getStorageClass() == SC_Extern;
- bool IsDefined = F->isDefined();
- if (!IsDefined && !IsExtern && DiagDecl->isConstexpr() &&
- S.checkingPotentialConstantExpression())
- return false;
+ // Silently reject constructors of invalid classes. The invalid class
+ // has been rejected elsewhere before.
+ if (CD && CD->getParent()->isInvalidDecl())
+ return false;
- // If the declaration is defined, declared 'constexpr' _and_ has a body,
- // the below diagnostic doesn't add anything useful.
- if (DiagDecl->isDefined() && DiagDecl->isConstexpr() &&
- DiagDecl->hasBody())
- return false;
+ // FIXME: If DiagDecl is an implicitly-declared special member function
+ // or an inheriting constructor, we should be much more explicit about why
+ // it's not constexpr.
+ if (CD && CD->isInheritingConstructor()) {
+ S.FFDiag(S.Current->getLocation(OpPC), diag::note_constexpr_invalid_inhctor,
+ 1)
+ << CD->getInheritedConstructor().getConstructor()->getParent();
+ S.Note(DiagDecl->getLocation(), diag::note_declared_at);
+ } else {
+ // Don't emit anything if the function isn't defined and we're checking
+ // for a constant expression. It might be defined at the point we're
+ // actually calling it.
+ bool IsExtern = DiagDecl->getStorageClass() == SC_Extern;
+ bool IsDefined = F->isDefined();
+ if (!IsDefined && !IsExtern && DiagDecl->isConstexpr() &&
+ S.checkingPotentialConstantExpression())
+ return false;
- S.FFDiag(S.Current->getLocation(OpPC),
- diag::note_constexpr_invalid_function, 1)
- << DiagDecl->isConstexpr() << (bool)CD << DiagDecl;
+ // If the declaration is defined, declared 'constexpr' _and_ has a body,
+ // the below diagnostic doesn't add anything useful.
+ if (DiagDecl->isDefined() && DiagDecl->isConstexpr() && DiagDecl->hasBody())
+ return false;
- if (DiagDecl->getDefinition())
- S.Note(DiagDecl->getDefinition()->getLocation(),
- diag::note_declared_at);
- else
- S.Note(DiagDecl->getLocation(), diag::note_declared_at);
- }
- } else {
S.FFDiag(S.Current->getLocation(OpPC),
- diag::note_invalid_subexpr_in_const_expr);
+ diag::note_constexpr_invalid_function, 1)
+ << DiagDecl->isConstexpr() << (bool)CD << DiagDecl;
+
+ if (DiagDecl->getDefinition())
+ S.Note(DiagDecl->getDefinition()->getLocation(), diag::note_declared_at);
+ else
+ S.Note(DiagDecl->getLocation(), diag::note_declared_at);
}
return false;
diff --git a/clang/test/AST/ByteCode/records.cpp b/clang/test/AST/ByteCode/records.cpp
index 00218ba02bb31..83f32c97c50c7 100644
--- a/clang/test/AST/ByteCode/records.cpp
+++ b/clang/test/AST/ByteCode/records.cpp
@@ -1861,3 +1861,24 @@ namespace PrimitiveInitializedByInitList {
} c{ 17 };
static_assert(c.b == 17, "");
}
+
+namespace MethodWillHaveBody {
+ class A {
+ public:
+ static constexpr int get_value2() { return 1 + get_value(); }
+ static constexpr int get_value() { return 1; }
+ };
+ static_assert(A::get_value2() == 2, "");
+
+ template<typename T> constexpr T f(T);
+ template<typename T> constexpr T g(T t) {
+ typedef int arr[f(T())]; // both-warning {{variable length array}} \
+ // both-note {{undefined function 'f<int>'}}
+ return t;
+ }
+ template<typename T> constexpr T f(T t) { // both-note {{declared here}}
+ typedef int arr[g(T())]; // both-note {{instantiation of}}
+ return t;
+ }
+ int n = f(0); // both-note {{instantiation of}}
+}
More information about the cfe-commits
mailing list