[clang] [clang][bytecode] Finish support for `msvc::constexpr` (PR #177388)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 22 08:04:22 PST 2026
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/177388
None
>From 5a02cecd85db32e5b86112bba2ad8b54560ca12c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 22 Jan 2026 16:40:11 +0100
Subject: [PATCH] [msvc::constexpr]]
---
clang/lib/AST/ByteCode/Compiler.cpp | 17 +++++++++++++++--
clang/lib/AST/ByteCode/Function.cpp | 2 +-
clang/lib/AST/ByteCode/Interp.cpp | 14 ++++++++++----
clang/lib/AST/ByteCode/Interp.h | 12 ++++++++++++
clang/lib/AST/ByteCode/InterpFrame.h | 3 +++
clang/lib/AST/ByteCode/Opcodes.td | 3 +++
clang/test/AST/ms-constexpr-new.cpp | 1 +
clang/test/SemaCXX/ms-constexpr-invalid.cpp | 3 +++
clang/test/SemaCXX/ms-constexpr-new.cpp | 4 ++++
clang/test/SemaCXX/ms-constexpr.cpp | 1 +
10 files changed, 53 insertions(+), 7 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index bbc0f5058e6f9..30e90ed00611b 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -6180,6 +6180,14 @@ bool Compiler<Emitter>::visitDefaultStmt(const DefaultStmt *S) {
template <class Emitter>
bool Compiler<Emitter>::visitAttributedStmt(const AttributedStmt *S) {
+ const Stmt *SubStmt = S->getSubStmt();
+
+ bool IsMSVCConstexprAttr = isa<ReturnStmt>(SubStmt) &&
+ hasSpecificAttr<MSConstexprAttr>(S->getAttrs());
+
+ if (IsMSVCConstexprAttr && !this->emitPushMSVCCE(S))
+ return false;
+
if (this->Ctx.getLangOpts().CXXAssumptions &&
!this->Ctx.getLangOpts().MSVCCompat) {
for (const Attr *A : S->getAttrs()) {
@@ -6187,7 +6195,7 @@ bool Compiler<Emitter>::visitAttributedStmt(const AttributedStmt *S) {
if (!AA)
continue;
- assert(isa<NullStmt>(S->getSubStmt()));
+ assert(isa<NullStmt>(SubStmt));
const Expr *Assumption = AA->getAssumption();
if (Assumption->isValueDependent())
@@ -6206,7 +6214,12 @@ bool Compiler<Emitter>::visitAttributedStmt(const AttributedStmt *S) {
}
// Ignore other attributes.
- return this->visitStmt(S->getSubStmt());
+ if (!this->visitStmt(SubStmt))
+ return false;
+
+ if (IsMSVCConstexprAttr)
+ return this->emitPopMSVCCE(S);
+ return true;
}
template <class Emitter>
diff --git a/clang/lib/AST/ByteCode/Function.cpp b/clang/lib/AST/ByteCode/Function.cpp
index a513be56ac0f8..4c7872b19dcdf 100644
--- a/clang/lib/AST/ByteCode/Function.cpp
+++ b/clang/lib/AST/ByteCode/Function.cpp
@@ -28,7 +28,7 @@ Function::Function(Program &P, FunctionDeclTy Source, unsigned ArgSize,
if (const auto *F = dyn_cast<const FunctionDecl *>(Source)) {
Variadic = F->isVariadic();
Immediate = F->isImmediateFunction();
- Constexpr = F->isConstexpr() || F->hasAttr<MSConstexprAttr>();
+ Constexpr = F->isConstexpr();
if (const auto *CD = dyn_cast<CXXConstructorDecl>(F)) {
Virtual = CD->isVirtual();
Kind = FunctionKind::Ctor;
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 4a98de1e75ecc..b61ff1dfd1691 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1042,7 +1042,9 @@ static bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
if (S.checkingPotentialConstantExpression() && S.Current->getDepth() != 0)
return false;
- if (F->isValid() && F->hasBody() && F->isConstexpr())
+ if (F->isValid() && F->hasBody() &&
+ (F->isConstexpr() || (S.Current->MSVCConstexprAllowed &&
+ F->getDecl()->hasAttr<MSConstexprAttr>())))
return true;
const FunctionDecl *DiagDecl = F->getDecl();
@@ -1551,7 +1553,8 @@ bool CheckFunctionDecl(InterpState &S, CodePtr OpPC, const FunctionDecl *FD) {
const Stmt *Body = FD->getBody(Definition);
if (Definition && Body &&
- (Definition->isConstexpr() || Definition->hasAttr<MSConstexprAttr>()))
+ (Definition->isConstexpr() || (S.Current->MSVCConstexprAllowed &&
+ Definition->hasAttr<MSConstexprAttr>())))
return true;
return diagnoseCallableDecl(S, OpPC, FD);
@@ -2062,9 +2065,12 @@ bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E) {
const FunctionDecl *OperatorNew = NewExpr->getOperatorNew();
if (NewExpr->getNumPlacementArgs() > 0) {
- // This is allowed pre-C++26, but only an std function.
- if (S.getLangOpts().CPlusPlus26 || S.Current->isStdFunction())
+ // This is allowed pre-C++26, but only an std function or if
+ // [[msvc::constexpr]] was used.
+ if (S.getLangOpts().CPlusPlus26 || S.Current->isStdFunction() ||
+ (S.Current->MSVCConstexprAllowed))
return true;
+
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_new_placement)
<< /*C++26 feature*/ 1 << E->getSourceRange();
} else if (
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 2d8cccb8095a4..0865d1824c311 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -3290,6 +3290,18 @@ inline bool PopCC(InterpState &S, CodePtr OpPC) {
return true;
}
+inline bool PushMSVCCE(InterpState &S, CodePtr OpPC) {
+ // This is a per-frame property.
+ ++S.Current->MSVCConstexprAllowed;
+ return true;
+}
+
+inline bool PopMSVCCE(InterpState &S, CodePtr OpPC) {
+ // This is a per-frame property.
+ --S.Current->MSVCConstexprAllowed;
+ return true;
+}
+
/// Do nothing and just abort execution.
inline bool Error(InterpState &S, CodePtr OpPC) { return false; }
diff --git a/clang/lib/AST/ByteCode/InterpFrame.h b/clang/lib/AST/ByteCode/InterpFrame.h
index e150e9279a6ef..61c1065e5848a 100644
--- a/clang/lib/AST/ByteCode/InterpFrame.h
+++ b/clang/lib/AST/ByteCode/InterpFrame.h
@@ -192,6 +192,9 @@ class InterpFrame final : public Frame {
const size_t FrameOffset;
/// Mapping from arg offsets to their argument blocks.
llvm::DenseMap<unsigned, std::unique_ptr<char[]>> Params;
+
+public:
+ unsigned MSVCConstexprAllowed = 0;
};
} // namespace interp
diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td
index 6e768793fcfcf..a3b0275ebd2ea 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -925,3 +925,6 @@ def CheckDestruction : Opcode;
def PushCC : Opcode { let Args = [ArgBool]; }
def PopCC : Opcode;
+
+def PushMSVCCE : Opcode;
+def PopMSVCCE : Opcode;
diff --git a/clang/test/AST/ms-constexpr-new.cpp b/clang/test/AST/ms-constexpr-new.cpp
index 4b534cf020764..f39b2594189cf 100644
--- a/clang/test/AST/ms-constexpr-new.cpp
+++ b/clang/test/AST/ms-constexpr-new.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++20 -ast-dump %s | FileCheck %s
+// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++20 -ast-dump %s -fexperimental-new-constant-interpreter | FileCheck %s
// CHECK: used operator new
// CHECK: MSConstexprAttr 0x{{[0-9a-f]+}} <col:17, col:23>
diff --git a/clang/test/SemaCXX/ms-constexpr-invalid.cpp b/clang/test/SemaCXX/ms-constexpr-invalid.cpp
index 89102c6fb954f..5405d3dc77aef 100644
--- a/clang/test/SemaCXX/ms-constexpr-invalid.cpp
+++ b/clang/test/SemaCXX/ms-constexpr-invalid.cpp
@@ -1,5 +1,8 @@
// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++20 -verify %s
// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++17 -verify %s
+//
+// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++20 -verify %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++17 -verify %s -fexperimental-new-constant-interpreter
// Check explicitly invalid code
diff --git a/clang/test/SemaCXX/ms-constexpr-new.cpp b/clang/test/SemaCXX/ms-constexpr-new.cpp
index 08794565f91df..a311b90bda730 100644
--- a/clang/test/SemaCXX/ms-constexpr-new.cpp
+++ b/clang/test/SemaCXX/ms-constexpr-new.cpp
@@ -1,5 +1,9 @@
// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++20 -verify=supported %s
// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.32 -std=c++20 -verify=unsupported %s
+
+// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++20 -verify=supported %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.32 -std=c++20 -verify=unsupported %s -fexperimental-new-constant-interpreter
+
// supported-no-diagnostics
[[nodiscard]]
diff --git a/clang/test/SemaCXX/ms-constexpr.cpp b/clang/test/SemaCXX/ms-constexpr.cpp
index 79f71a34cb7d8..9beaf3d5a7d8c 100644
--- a/clang/test/SemaCXX/ms-constexpr.cpp
+++ b/clang/test/SemaCXX/ms-constexpr.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++20 -verify %s
+// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++20 -verify %s -fexperimental-new-constant-interpreter
[[msvc::constexpr]] int log2(int x) { [[msvc::constexpr]] return x > 1 ? 1 + log2(x / 2) : 0; }
constexpr bool test_log2() { [[msvc::constexpr]] return log2(32) == 5; }
More information about the cfe-commits
mailing list