Skip to content

Commit b3e6ff3

Browse files
authored
[clang-cl] Add support for [[msvc::constexpr]] C++11 attribute (#71300)
This commit introduces support for the MSVC-specific C++11-style attribute `[[msvc::constexpr]]`, which was introduced in MSVC 14.33. The semantics of this attribute are enabled only under MSVC compatibility (`-fms-compatibility-version`) 14.33 and higher. Additionally, the default value of `_MSC_VER` has been raised to 1433. The current implementation lacks support for: - `[[msvc::constexpr]]` constructors (see #72149); at the time of this implementation, such support would have required an unreasonable number of changes in Clang. - `[[msvc::constexpr]] return ::new` (constexpr placement new) from non-std namespaces (see #74924). Relevant to: #57696
1 parent b3000ec commit b3e6ff3

18 files changed

+239
-13
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ Non-comprehensive list of changes in this release
238238
except that it returns the size of a type ignoring tail padding.
239239
* ``__builtin_classify_type()`` now classifies ``_BitInt`` values as the return value ``18``
240240
and vector types as return value ``19``, to match GCC 14's behavior.
241+
* The default value of `_MSC_VER` was raised from 1920 to 1933.
242+
* Since MSVC 19.33 added undocumented attribute ``[[msvc::constexpr]]``, this release adds the attribute as well.
241243

242244
* Added ``#pragma clang fp reciprocal``.
243245

clang/docs/UsersManual.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3359,8 +3359,8 @@ default for Windows targets.
33593359

33603360
For compatibility with existing code that compiles with MSVC, clang defines the
33613361
``_MSC_VER`` and ``_MSC_FULL_VER`` macros. When on Windows, these default to
3362-
either the same value as the currently installed version of cl.exe, or ``1920``
3363-
and ``192000000`` (respectively). The ``-fms-compatibility-version=`` flag
3362+
either the same value as the currently installed version of cl.exe, or ``1933``
3363+
and ``193300000`` (respectively). The ``-fms-compatibility-version=`` flag
33643364
overrides these values. It accepts a dotted version tuple, such as 19.00.23506.
33653365
Changing the MSVC compatibility version makes clang behave more like that
33663366
version of MSVC. For example, ``-fms-compatibility-version=19`` will enable

clang/include/clang/Basic/Attr.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3646,6 +3646,14 @@ def : MutualExclusions<[Owner, Pointer]>;
36463646

36473647
// Microsoft-related attributes
36483648

3649+
def MSConstexpr : InheritableAttr {
3650+
let LangOpts = [MicrosoftExt];
3651+
let Spellings = [CXX11<"msvc", "constexpr">];
3652+
let Subjects = SubjectList<[Function, ReturnStmt], ErrorDiag,
3653+
"functions and return statements">;
3654+
let Documentation = [MSConstexprDocs];
3655+
}
3656+
36493657
def MSNoVTable : InheritableAttr, TargetSpecificAttr<TargetMicrosoftCXXABI> {
36503658
let Spellings = [Declspec<"novtable">];
36513659
let Subjects = SubjectList<[CXXRecord]>;

clang/include/clang/Basic/AttrDocs.td

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3657,6 +3657,21 @@ an error:
36573657
}];
36583658
}
36593659

3660+
def MSConstexprDocs : Documentation {
3661+
let Category = DocCatStmt;
3662+
let Content = [{
3663+
The ``[[msvc::constexpr]]`` attribute can be applied only to a function
3664+
definition or a ``return`` statement. It does not impact function declarations.
3665+
A ``[[msvc::constexpr]]`` function cannot be ``constexpr`` or ``consteval``.
3666+
A ``[[msvc::constexpr]]`` function is treated as if it were a ``constexpr`` function
3667+
when it is evaluated in a constant context of ``[[msvc::constexpr]] return`` statement.
3668+
Otherwise, it is treated as a regular function.
3669+
3670+
Semantics of this attribute are enabled only under MSVC compatibility
3671+
(``-fms-compatibility-version``) 19.33 and later.
3672+
}];
3673+
}
3674+
36603675
def MSNoVTableDocs : Documentation {
36613676
let Category = DocCatDecl;
36623677
let Content = [{

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2884,6 +2884,8 @@ def warn_cxx11_compat_constexpr_body_multiple_return : Warning<
28842884
InGroup<CXXPre14Compat>, DefaultIgnore;
28852885
def note_constexpr_body_previous_return : Note<
28862886
"previous return statement is here">;
2887+
def err_ms_constexpr_cannot_be_applied : Error<
2888+
"attribute 'msvc::constexpr' cannot be applied to the %select{constexpr|consteval|virtual}0 function %1">;
28872889

28882890
// C++20 function try blocks in constexpr
28892891
def ext_constexpr_function_try_block_cxx20 : ExtWarn<

clang/include/clang/Basic/LangOptions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ class LangOptions : public LangOptionsBase {
152152
MSVC2019 = 1920,
153153
MSVC2019_5 = 1925,
154154
MSVC2019_8 = 1928,
155+
MSVC2022_3 = 1933,
155156
};
156157

157158
enum SYCLMajorVersion {

clang/lib/AST/ExprConstant.cpp

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,10 @@ namespace {
641641
return false;
642642
}
643643

644+
/// Whether we're in a context where [[msvc::constexpr]] evaluation is
645+
/// permitted. See MSConstexprDocs for description of permitted contexts.
646+
bool CanEvalMSConstexpr = false;
647+
644648
private:
645649
APValue &createLocal(APValue::LValueBase Base, const void *Key, QualType T,
646650
ScopeKind Scope);
@@ -674,6 +678,19 @@ namespace {
674678
private:
675679
llvm::TimeTraceScope TimeScope;
676680
};
681+
682+
/// RAII object used to change the current ability of
683+
/// [[msvc::constexpr]] evaulation.
684+
struct MSConstexprContextRAII {
685+
CallStackFrame &Frame;
686+
bool OldValue;
687+
explicit MSConstexprContextRAII(CallStackFrame &Frame, bool Value)
688+
: Frame(Frame), OldValue(Frame.CanEvalMSConstexpr) {
689+
Frame.CanEvalMSConstexpr = Value;
690+
}
691+
692+
~MSConstexprContextRAII() { Frame.CanEvalMSConstexpr = OldValue; }
693+
};
677694
}
678695

679696
static bool HandleDestruction(EvalInfo &Info, const Expr *E,
@@ -5546,11 +5563,14 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
55465563
case Stmt::LabelStmtClass:
55475564
return EvaluateStmt(Result, Info, cast<LabelStmt>(S)->getSubStmt(), Case);
55485565

5549-
case Stmt::AttributedStmtClass:
5550-
// As a general principle, C++11 attributes can be ignored without
5551-
// any semantic impact.
5552-
return EvaluateStmt(Result, Info, cast<AttributedStmt>(S)->getSubStmt(),
5553-
Case);
5566+
case Stmt::AttributedStmtClass: {
5567+
const auto *AS = cast<AttributedStmt>(S);
5568+
const auto *SS = AS->getSubStmt();
5569+
MSConstexprContextRAII ConstexprContext(
5570+
*Info.CurrentCall, hasSpecificAttr<MSConstexprAttr>(AS->getAttrs()) &&
5571+
isa<ReturnStmt>(SS));
5572+
return EvaluateStmt(Result, Info, SS, Case);
5573+
}
55545574

55555575
case Stmt::CaseStmtClass:
55565576
case Stmt::DefaultStmtClass:
@@ -5621,7 +5641,9 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc,
56215641
}
56225642

56235643
// Can we evaluate this function call?
5624-
if (Definition && Definition->isConstexpr() && Body)
5644+
if (Definition && Body &&
5645+
(Definition->isConstexpr() || Info.CurrentCall->CanEvalMSConstexpr &&
5646+
Definition->hasAttr<MSConstexprAttr>()))
56255647
return true;
56265648

56275649
if (Info.getLangOpts().CPlusPlus11) {

clang/lib/Basic/Targets/OSTargets.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,9 @@ static void addVisualCDefines(const LangOptions &Opts, MacroBuilder &Builder) {
224224
else if (Opts.CPlusPlus14)
225225
Builder.defineMacro("_MSVC_LANG", "201402L");
226226
}
227+
228+
if (Opts.isCompatibleWithMSVC(LangOptions::MSVC2022_3))
229+
Builder.defineMacro("_MSVC_CONSTEXPR_ATTRIBUTE");
227230
}
228231

229232
if (Opts.MicrosoftExt) {

clang/lib/Driver/ToolChains/MSVC.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -787,11 +787,11 @@ VersionTuple MSVCToolChain::computeMSVCVersion(const Driver *D,
787787
if (MSVT.empty() &&
788788
Args.hasFlag(options::OPT_fms_extensions, options::OPT_fno_ms_extensions,
789789
IsWindowsMSVC)) {
790-
// -fms-compatibility-version=19.20 is default, aka 2019, 16.x
790+
// -fms-compatibility-version=19.33 is default, aka 2022, 17.3
791791
// NOTE: when changing this value, also update
792792
// clang/docs/CommandGuide/clang.rst and clang/docs/UsersManual.rst
793793
// accordingly.
794-
MSVT = VersionTuple(19, 20);
794+
MSVT = VersionTuple(19, 33);
795795
}
796796
return MSVT;
797797
}

clang/lib/Sema/SemaDecl.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16221,7 +16221,9 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
1622116221
ActivePolicy = &WP;
1622216222
}
1622316223

16224-
if (!IsInstantiation && FD && FD->isConstexpr() && !FD->isInvalidDecl() &&
16224+
if (!IsInstantiation && FD &&
16225+
(FD->isConstexpr() || FD->hasAttr<MSConstexprAttr>()) &&
16226+
!FD->isInvalidDecl() &&
1622516227
!CheckConstexprFunctionDefinition(FD, CheckConstexprKind::Diagnose))
1622616228
FD->setInvalidDecl();
1622716229

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7372,6 +7372,28 @@ static void handleDeclspecThreadAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
73727372
D->addAttr(::new (S.Context) ThreadAttr(S.Context, AL));
73737373
}
73747374

7375+
static void handleMSConstexprAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
7376+
if (!S.getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2022_3)) {
7377+
S.Diag(AL.getLoc(), diag::warn_unknown_attribute_ignored)
7378+
<< AL << AL.getRange();
7379+
return;
7380+
}
7381+
auto *FD = cast<FunctionDecl>(D);
7382+
if (FD->isConstexprSpecified() || FD->isConsteval()) {
7383+
S.Diag(AL.getLoc(), diag::err_ms_constexpr_cannot_be_applied)
7384+
<< FD->isConsteval() << FD;
7385+
return;
7386+
}
7387+
if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
7388+
if (!S.getLangOpts().CPlusPlus20 && MD->isVirtual()) {
7389+
S.Diag(AL.getLoc(), diag::err_ms_constexpr_cannot_be_applied)
7390+
<< /*virtual*/ 2 << MD;
7391+
return;
7392+
}
7393+
}
7394+
D->addAttr(::new (S.Context) MSConstexprAttr(S.Context, AL));
7395+
}
7396+
73757397
static void handleAbiTagAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
73767398
SmallVector<StringRef, 4> Tags;
73777399
for (unsigned I = 0, E = AL.getNumArgs(); I != E; ++I) {
@@ -9477,6 +9499,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
94779499
case ParsedAttr::AT_Thread:
94789500
handleDeclspecThreadAttr(S, D, AL);
94799501
break;
9502+
case ParsedAttr::AT_MSConstexpr:
9503+
handleMSConstexprAttr(S, D, AL);
9504+
break;
94809505

94819506
// HLSL attributes:
94829507
case ParsedAttr::AT_HLSLNumThreads:

clang/lib/Sema/SemaStmtAttr.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,16 @@ static void CheckForDuplicateCodeAlignAttrs(Sema &S,
397397
}
398398
}
399399

400+
static Attr *handleMSConstexprAttr(Sema &S, Stmt *St, const ParsedAttr &A,
401+
SourceRange Range) {
402+
if (!S.getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2022_3)) {
403+
S.Diag(A.getLoc(), diag::warn_unknown_attribute_ignored)
404+
<< A << A.getRange();
405+
return nullptr;
406+
}
407+
return ::new (S.Context) MSConstexprAttr(S.Context, A);
408+
}
409+
400410
#define WANT_STMT_MERGE_LOGIC
401411
#include "clang/Sema/AttrParsedAttrImpl.inc"
402412
#undef WANT_STMT_MERGE_LOGIC
@@ -600,6 +610,8 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
600610
return handleUnlikely(S, St, A, Range);
601611
case ParsedAttr::AT_CodeAlign:
602612
return handleCodeAlignAttr(S, St, A);
613+
case ParsedAttr::AT_MSConstexpr:
614+
return handleMSConstexprAttr(S, St, A, Range);
603615
default:
604616
// N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a
605617
// declaration attribute is not written on a statement, but this code is

clang/test/AST/ms-constexpr.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++20 -ast-dump -verify %s | FileCheck %s
2+
// expected-no-diagnostics
3+
4+
// CHECK: used f1 'bool ()'
5+
// CHECK: MSConstexprAttr 0x{{[0-9a-f]+}} <col:3, col:9>
6+
[[msvc::constexpr]] bool f1() { return true; }
7+
8+
// CHECK: used constexpr f2 'bool ()'
9+
// CHECK-NEXT: CompoundStmt 0x{{[0-9a-f]+}} <col:21, col:56>
10+
// CHECK-NEXT: AttributedStmt 0x{{[0-9a-f]+}} <col:23, col:53>
11+
// CHECK-NEXT: MSConstexprAttr 0x{{[0-9a-f]+}} <col:25, col:31>
12+
// CHECK-NEXT: ReturnStmt 0x{{[0-9a-f]+}} <col:43, col:53>
13+
constexpr bool f2() { [[msvc::constexpr]] return f1(); }
14+
static_assert(f2());
15+
16+
struct S1 {
17+
// CHECK: used vm 'bool ()' virtual
18+
// CHECK: MSConstexprAttr 0x{{[0-9a-f]+}} <col:7, col:13>
19+
[[msvc::constexpr]] virtual bool vm() { return true; }
20+
21+
// CHECK: used constexpr cm 'bool ()'
22+
// CHECK-NEXT: CompoundStmt 0x{{[0-9a-f]+}} <col:25, col:60>
23+
// CHECK-NEXT: AttributedStmt 0x{{[0-9a-f]+}} <col:27, col:57>
24+
// CHECK-NEXT: MSConstexprAttr 0x{{[0-9a-f]+}} <col:29, col:35>
25+
// CHECK-NEXT: ReturnStmt 0x{{[0-9a-f]+}} <col:47, col:57>
26+
constexpr bool cm() { [[msvc::constexpr]] return vm(); }
27+
};
28+
static_assert(S1{}.cm());

clang/test/Driver/cl-options.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -747,7 +747,7 @@
747747

748748
// Validate that the default triple is used when run an empty tools dir is specified
749749
// RUN: %clang_cl -vctoolsdir "" -### -- %s 2>&1 | FileCheck %s --check-prefix VCTOOLSDIR
750-
// VCTOOLSDIR: "-triple" "{{[a-zA-Z0-9_-]*}}-pc-windows-msvc19.20.0"
750+
// VCTOOLSDIR: "-triple" "{{[a-zA-Z0-9_-]*}}-pc-windows-msvc19.33.0"
751751

752752
// Validate that built-in include paths are based on the supplied path
753753
// RUN: %clang_cl --target=aarch64-pc-windows-msvc -vctoolsdir "/fake" -winsdkdir "/foo" -winsdkversion 10.0.12345.0 -### -- %s 2>&1 | FileCheck %s --check-prefix FAKEDIR
@@ -787,7 +787,7 @@
787787

788788
// RUN: %clang_cl -vctoolsdir "" /arm64EC /c -### -- %s 2>&1 | FileCheck --check-prefix=ARM64EC %s
789789
// ARM64EC-NOT: /arm64EC has been overridden by specified target
790-
// ARM64EC: "-triple" "arm64ec-pc-windows-msvc19.20.0"
790+
// ARM64EC: "-triple" "arm64ec-pc-windows-msvc19.33.0"
791791

792792
// RUN: %clang_cl -vctoolsdir "" /arm64EC /c -target x86_64-pc-windows-msvc -### -- %s 2>&1 | FileCheck --check-prefix=ARM64EC_OVERRIDE %s
793793
// ARM64EC_OVERRIDE: warning: /arm64EC has been overridden by specified target: x86_64-pc-windows-msvc; option ignored

clang/test/Misc/pragma-attribute-supported-attributes-list.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
// CHECK-NEXT: LoaderUninitialized (SubjectMatchRule_variable_is_global)
9191
// CHECK-NEXT: Lockable (SubjectMatchRule_record)
9292
// CHECK-NEXT: MIGServerRoutine (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_block)
93+
// CHECK-NEXT: MSConstexpr (SubjectMatchRule_function)
9394
// CHECK-NEXT: MSStruct (SubjectMatchRule_record)
9495
// CHECK-NEXT: MaybeUndef (SubjectMatchRule_variable_is_parameter)
9596
// CHECK-NEXT: MicroMips (SubjectMatchRule_function)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++20 -verify %s
2+
// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++17 -verify %s
3+
4+
// Check explicitly invalid code
5+
6+
void runtime() {} // expected-note {{declared here}}
7+
8+
[[msvc::constexpr]] void f0() { runtime(); } // expected-error {{constexpr function never produces a constant expression}} \
9+
// expected-note {{non-constexpr function 'runtime' cannot be used in a constant expression}}
10+
[[msvc::constexpr]] constexpr void f1() {} // expected-error {{attribute 'msvc::constexpr' cannot be applied to the constexpr function 'f1'}}
11+
#if __cplusplus >= 202202L
12+
[[msvc::constexpr]] consteval void f2() {} // expected-error {{attribute 'msvc::constexpr' cannot be applied to the consteval function 'f1'}}
13+
#endif
14+
15+
struct B1 {};
16+
struct D1 : virtual B1 { // expected-note {{virtual base class declared here}}
17+
[[msvc::constexpr]] D1() {} // expected-error {{constexpr constructor not allowed in struct with virtual base class}}
18+
};
19+
20+
struct [[msvc::constexpr]] S2{}; // expected-error {{'constexpr' attribute only applies to functions and return statements}}
21+
22+
// Check invalid code mixed with valid code
23+
24+
[[msvc::constexpr]] int f4(int x) { return x > 1 ? 1 + f4(x / 2) : 0; } // expected-note {{non-constexpr function 'f4' cannot be used in a constant expression}} \
25+
// expected-note {{declared here}} \
26+
// expected-note {{declared here}} \
27+
// expected-note {{declared here}}
28+
constexpr bool f5() { [[msvc::constexpr]] return f4(32) == 5; } // expected-note {{in call to 'f4(32)'}}
29+
static_assert(f5()); // expected-error {{static assertion expression is not an integral constant expression}} \
30+
// expected-note {{in call to 'f5()'}}
31+
32+
int f6(int x) { [[msvc::constexpr]] return x > 1 ? 1 + f6(x / 2) : 0; } // expected-note {{declared here}} \
33+
// expected-note {{declared here}}
34+
constexpr bool f7() { [[msvc::constexpr]] return f6(32) == 5; } // expected-error {{constexpr function never produces a constant expression}} \
35+
// expected-note {{non-constexpr function 'f6' cannot be used in a constant expression}} \
36+
// expected-note {{non-constexpr function 'f6' cannot be used in a constant expression}}
37+
static_assert(f7()); // expected-error {{static assertion expression is not an integral constant expression}} \
38+
// expected-note {{in call to 'f7()'}}
39+
40+
constexpr bool f8() { // expected-error {{constexpr function never produces a constant expression}}
41+
[[msvc::constexpr]] f4(32); // expected-error {{'constexpr' attribute only applies to functions and return statements}} \
42+
// expected-note {{non-constexpr function 'f4' cannot be used in a constant expression}} \
43+
// expected-note {{non-constexpr function 'f4' cannot be used in a constant expression}}
44+
[[msvc::constexpr]] int i5 = f4(32); // expected-error {{'constexpr' attribute only applies to functions and return statements}}
45+
return i5 == 5;
46+
}
47+
static_assert(f8()); // expected-error {{static assertion expression is not an integral constant expression}} \
48+
// expected-note {{in call to 'f8()'}}
49+
50+
#if __cplusplus == 201702L
51+
struct S1 { [[msvc::constexpr]] virtual bool vm() const { return true; } }; // expected-error {{attribute 'msvc::constexpr' ignored, it only applies to function definitions and return statements}}
52+
#endif
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++20 -verify=supported %s
2+
// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.32 -std=c++20 -verify=unsupported %s
3+
// supported-no-diagnostics
4+
5+
[[nodiscard]]
6+
[[msvc::constexpr]] // unsupported-warning {{unknown attribute 'constexpr' ignored}}
7+
inline void* __cdecl operator new(decltype(sizeof(void*)), void* p) noexcept { return p; }
8+
9+
namespace std {
10+
constexpr int* construct_at(int* p, int v) {
11+
[[msvc::constexpr]] return ::new (p) int(v); // unsupported-warning {{unknown attribute 'constexpr' ignored}}
12+
}
13+
}
14+
15+
constexpr bool check_construct_at() { int x; return *std::construct_at(&x, 42) == 42; }
16+
static_assert(check_construct_at());

clang/test/SemaCXX/ms-constexpr.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++20 -verify %s
2+
3+
[[msvc::constexpr]] int log2(int x) { [[msvc::constexpr]] return x > 1 ? 1 + log2(x / 2) : 0; }
4+
constexpr bool test_log2() { [[msvc::constexpr]] return log2(32) == 5; }
5+
static_assert(test_log2());
6+
7+
[[msvc::constexpr]] int get_value(int x)
8+
{
9+
switch (x)
10+
{
11+
case 42: return 1337;
12+
default:
13+
if (x < 0) [[msvc::constexpr]] return log2(-x);
14+
else return x;
15+
}
16+
}
17+
18+
constexpr bool test_complex_expr() {
19+
[[msvc::constexpr]] return get_value(get_value(42) - 1337 + get_value(-32) - 5 + (get_value(1) ? get_value(0) : get_value(2))) == get_value(0);
20+
}
21+
static_assert(test_complex_expr());
22+
23+
constexpr bool get_constexpr_true() { return true; }
24+
[[msvc::constexpr]] bool get_msconstexpr_true() { return get_constexpr_true(); }
25+
constexpr bool test_get_msconstexpr_true() { [[msvc::constexpr]] return get_msconstexpr_true(); }
26+
static_assert(test_get_msconstexpr_true());
27+
28+
// TODO (#72149): Add support for [[msvc::constexpr]] constructor; this code is valid for MSVC.
29+
struct S2 {
30+
[[msvc::constexpr]] S2() {}
31+
[[msvc::constexpr]] bool value() { return true; }
32+
static constexpr bool check() { [[msvc::constexpr]] return S2{}.value(); } // expected-error {{constexpr function never produces a constant expression}} \
33+
// expected-note {{non-literal type 'S2' cannot be used in a constant expression}} \
34+
// expected-note {{non-literal type 'S2' cannot be used in a constant expression}}
35+
};
36+
static_assert(S2::check()); // expected-error {{static assertion expression is not an integral constant expression}} \
37+
// expected-note {{in call to 'check()'}}

0 commit comments

Comments
 (0)