[clang] [clang][bytecode] Use Param decl as variable source if we can (PR #152909)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Sun Aug 10 04:21:19 PDT 2025
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/152909
This results in diagnostics that are closer to what the current interpreter produces.
>From 711115ab80b6957d09ac8dd7a4fac1a32f3b3744 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Sun, 10 Aug 2025 13:05:49 +0200
Subject: [PATCH] [clang][bytecode] Use Param decl as variable source if we can
This results in diagnostics that are closer to what the current
interpreter produces.
---
clang/lib/AST/ByteCode/Compiler.cpp | 40 +++++++++----------
clang/lib/AST/ByteCode/Compiler.h | 2 +-
clang/test/AST/ByteCode/cxx23.cpp | 5 +--
clang/test/AST/ByteCode/lifetimes.cpp | 9 ++---
.../test/SemaCXX/cxx23-invalid-constexpr.cpp | 1 +
5 files changed, 28 insertions(+), 29 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index f131ac17c11bb..5275b86a57a47 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -2062,12 +2062,16 @@ bool Compiler<Emitter>::visitArrayElemInit(unsigned ElemIndex, const Expr *Init,
template <class Emitter>
bool Compiler<Emitter>::visitCallArgs(ArrayRef<const Expr *> Args,
const FunctionDecl *FuncDecl,
- bool Activate) {
+ bool Activate, bool IsOperatorCall) {
assert(VarScope->getKind() == ScopeKind::Call);
llvm::BitVector NonNullArgs;
if (FuncDecl && FuncDecl->hasAttr<NonNullAttr>())
NonNullArgs = collectNonNullArgs(FuncDecl, Args);
+ bool ExplicitMemberFn = false;
+ if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>(FuncDecl))
+ ExplicitMemberFn = MD->isExplicitObjectMemberFunction();
+
unsigned ArgIndex = 0;
for (const Expr *Arg : Args) {
if (canClassify(Arg)) {
@@ -2075,8 +2079,19 @@ bool Compiler<Emitter>::visitCallArgs(ArrayRef<const Expr *> Args,
return false;
} else {
- std::optional<unsigned> LocalIndex = allocateLocal(
- Arg, Arg->getType(), /*ExtendingDecl=*/nullptr, ScopeKind::Call);
+ DeclTy Source = Arg;
+ if (FuncDecl) {
+ // Try to use the parameter declaration instead of the argument
+ // expression as a source.
+ unsigned DeclIndex = ArgIndex - IsOperatorCall + ExplicitMemberFn;
+ if (DeclIndex < FuncDecl->getNumParams())
+ Source = FuncDecl->getParamDecl(ArgIndex - IsOperatorCall +
+ ExplicitMemberFn);
+ }
+
+ std::optional<unsigned> LocalIndex =
+ allocateLocal(std::move(Source), Arg->getType(),
+ /*ExtendingDecl=*/nullptr, ScopeKind::Call);
if (!LocalIndex)
return false;
@@ -4489,14 +4504,6 @@ template <class Emitter>
unsigned Compiler<Emitter>::allocateLocalPrimitive(
DeclTy &&Src, PrimType Ty, bool IsConst, const ValueDecl *ExtendingDecl,
ScopeKind SC, bool IsConstexprUnknown) {
- // Make sure we don't accidentally register the same decl twice.
- if (const auto *VD =
- dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>())) {
- assert(!P.getGlobal(VD));
- assert(!Locals.contains(VD));
- (void)VD;
- }
-
// FIXME: There are cases where Src.is<Expr*>() is wrong, e.g.
// (int){12} in C. Consider using Expr::isTemporaryObject() instead
// or isa<MaterializeTemporaryExpr>().
@@ -4518,19 +4525,11 @@ std::optional<unsigned>
Compiler<Emitter>::allocateLocal(DeclTy &&Src, QualType Ty,
const ValueDecl *ExtendingDecl, ScopeKind SC,
bool IsConstexprUnknown) {
- // Make sure we don't accidentally register the same decl twice.
- if ([[maybe_unused]] const auto *VD =
- dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>())) {
- assert(!P.getGlobal(VD));
- assert(!Locals.contains(VD));
- }
-
const ValueDecl *Key = nullptr;
const Expr *Init = nullptr;
bool IsTemporary = false;
if (auto *VD = dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>())) {
Key = VD;
- Ty = VD->getType();
if (const auto *VarD = dyn_cast<VarDecl>(VD))
Init = VarD->getInit();
@@ -5167,7 +5166,8 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
return false;
}
- if (!this->visitCallArgs(Args, FuncDecl, IsAssignmentOperatorCall))
+ if (!this->visitCallArgs(Args, FuncDecl, IsAssignmentOperatorCall,
+ isa<CXXOperatorCallExpr>(E)))
return false;
// Undo the argument reversal we did earlier.
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index d72ffa13f6b9d..901934e530ade 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -306,7 +306,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
bool visitArrayElemInit(unsigned ElemIndex, const Expr *Init,
OptPrimType InitT);
bool visitCallArgs(ArrayRef<const Expr *> Args, const FunctionDecl *FuncDecl,
- bool Activate);
+ bool Activate, bool IsOperatorCall);
/// Creates a local primitive value.
unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsConst,
diff --git a/clang/test/AST/ByteCode/cxx23.cpp b/clang/test/AST/ByteCode/cxx23.cpp
index 45dd4f528aefb..2182d7c4e4325 100644
--- a/clang/test/AST/ByteCode/cxx23.cpp
+++ b/clang/test/AST/ByteCode/cxx23.cpp
@@ -309,15 +309,14 @@ namespace NonLiteralDtorInParam {
~NonLiteral() {} // all23-note {{declared here}}
};
constexpr int F2(NonLiteral N) { // all20-error {{constexpr function's 1st parameter type 'NonLiteral' is not a literal type}} \
- // ref23-note {{non-constexpr function '~NonLiteral' cannot be used in a constant expression}}
+ // all23-note {{non-constexpr function '~NonLiteral' cannot be used in a constant expression}}
return 8;
}
void test() {
NonLiteral L;
- constexpr auto D = F2(L); // all23-error {{must be initialized by a constant expression}} \
- // expected23-note {{non-constexpr function '~NonLiteral' cannot be used in a constant expression}}
+ constexpr auto D = F2(L); // all23-error {{must be initialized by a constant expression}}
}
}
diff --git a/clang/test/AST/ByteCode/lifetimes.cpp b/clang/test/AST/ByteCode/lifetimes.cpp
index 5c8d56291f079..c8bf02c228481 100644
--- a/clang/test/AST/ByteCode/lifetimes.cpp
+++ b/clang/test/AST/ByteCode/lifetimes.cpp
@@ -94,14 +94,13 @@ namespace CallScope {
int n = 0;
constexpr int f() const { return 0; }
};
- constexpr Q *out_of_lifetime(Q q) { return &q; } // both-warning {{address of stack}}
+ constexpr Q *out_of_lifetime(Q q) { return &q; } // both-warning {{address of stack}} \
+ // expected-note 2{{declared here}}
constexpr int k3 = out_of_lifetime({})->n; // both-error {{must be initialized by a constant expression}} \
- // expected-note {{read of temporary whose lifetime has ended}} \
- // expected-note {{temporary created here}} \
+ // expected-note {{read of variable whose lifetime has ended}} \
// ref-note {{read of object outside its lifetime}}
constexpr int k4 = out_of_lifetime({})->f(); // both-error {{must be initialized by a constant expression}} \
- // expected-note {{member call on temporary whose lifetime has ended}} \
- // expected-note {{temporary created here}} \
+ // expected-note {{member call on variable whose lifetime has ended}} \
// ref-note {{member call on object outside its lifetime}}
}
diff --git a/clang/test/SemaCXX/cxx23-invalid-constexpr.cpp b/clang/test/SemaCXX/cxx23-invalid-constexpr.cpp
index 3229a91fbcefb..1c832e51c2cbc 100644
--- a/clang/test/SemaCXX/cxx23-invalid-constexpr.cpp
+++ b/clang/test/SemaCXX/cxx23-invalid-constexpr.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify=expected -std=c++23 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected -std=c++23 %s -fexperimental-new-constant-interpreter
// This test covers modifications made by P2448R2.
More information about the cfe-commits
mailing list