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
