Author: Timm Baeder
Date: 2026-01-23T11:25:21+01:00
New Revision: 323bb143491f61530097c8efd97f6269369e2e8d

URL: 
https://github.com/llvm/llvm-project/commit/323bb143491f61530097c8efd97f6269369e2e8d
DIFF: 
https://github.com/llvm/llvm-project/commit/323bb143491f61530097c8efd97f6269369e2e8d.diff

LOG: [clang][bytecode] Finish support for `msvc::constexpr` (#177388)

Keep track of whether an `InterpFrame` is allowed to call
`msvc::constexpr` functions via two new opcodes.

Added: 
    

Modified: 
    clang/lib/AST/ByteCode/Compiler.cpp
    clang/lib/AST/ByteCode/Function.cpp
    clang/lib/AST/ByteCode/Interp.cpp
    clang/lib/AST/ByteCode/Interp.h
    clang/lib/AST/ByteCode/InterpFrame.h
    clang/lib/AST/ByteCode/Opcodes.td
    clang/test/AST/ms-constexpr-new.cpp
    clang/test/SemaCXX/ms-constexpr-invalid.cpp
    clang/test/SemaCXX/ms-constexpr-new.cpp
    clang/test/SemaCXX/ms-constexpr.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 236f79c002301..272d08f5e455c 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 20cd03c93ff94..2a495a475c378 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();
@@ -1566,7 +1568,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);
@@ -2122,9 +2125,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 f6b7d96e6a192..b7de06f9a673e 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -3296,6 +3296,19 @@ 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) {
+  assert(S.Current->MSVCConstexprAllowed >= 1);
+  // 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 c3123410d06fe..e701c954e00c2 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -927,3 +927,6 @@ def CtorCheck : 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; }


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to