[clang][bytecode] Don't diagnose defined functions that will have a body (#165002)

Don't use `hasBody()`, which checks all declarations.

Fixes https://github.com/llvm/llvm-project/issues/164995
This commit is contained in:
Timm Baeder
2025-10-27 14:06:54 +01:00
committed by GitHub
parent f80b27349d
commit f29235d698
2 changed files with 84 additions and 57 deletions

View File

@@ -932,6 +932,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;
@@ -939,7 +948,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.
@@ -957,64 +966,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 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;
}
// Silently reject constructors of invalid classes. The invalid class
// has been rejected elsewhere before.
if (CD && CD->getParent()->isInvalidDecl())
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;
// 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;
S.FFDiag(S.Current->getLocation(OpPC),
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);
}
} else {
if (!S.getLangOpts().CPlusPlus11) {
S.FFDiag(S.Current->getLocation(OpPC),
diag::note_invalid_subexpr_in_const_expr);
return false;
}
// Invalid decls have been diagnosed before.
if (DiagDecl->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;
}
// Silently reject constructors of invalid classes. The invalid class
// has been rejected elsewhere before.
if (CD && CD->getParent()->isInvalidDecl())
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;
// 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;
S.FFDiag(S.Current->getLocation(OpPC),
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;

View File

@@ -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}}
}