[clang] [clang-cl] Add support for [[msvc::constexpr]] C++11 attribute (PR #71300)
Richard Dzenis via cfe-commits
cfe-commits at lists.llvm.org
Sat Nov 25 01:14:30 PST 2023
https://github.com/RIscRIpt updated https://github.com/llvm/llvm-project/pull/71300
>From 65c306e94e9b749ef0d38ef709ddb8b23dac987a Mon Sep 17 00:00:00 2001
From: Richard Dzenis <dzenis at richard.lv>
Date: Thu, 20 Jul 2023 00:18:50 +0300
Subject: [PATCH 1/2] [clang-cl] Add support for [[msvc::constexpr]] C++11
attribute
Differential Revision: https://reviews.llvm.org/D134475
---
clang/include/clang/Basic/Attr.td | 8 +++
clang/include/clang/Basic/AttrDocs.td | 16 ++++++
.../clang/Basic/DiagnosticSemaKinds.td | 10 +++-
clang/include/clang/Basic/LangOptions.h | 1 +
clang/lib/AST/ExprConstant.cpp | 34 +++++++++---
clang/lib/Basic/Targets/OSTargets.cpp | 3 ++
clang/lib/Sema/SemaDecl.cpp | 4 +-
clang/lib/Sema/SemaDeclAttr.cpp | 24 +++++++++
clang/lib/Sema/SemaDeclCXX.cpp | 5 +-
clang/lib/Sema/SemaStmtAttr.cpp | 16 ++++++
clang/test/AST/ms-constexpr.cpp | 28 ++++++++++
...a-attribute-supported-attributes-list.test | 1 +
clang/test/SemaCXX/ms-constexpr-invalid.cpp | 52 +++++++++++++++++++
clang/test/SemaCXX/ms-constexpr-new.cpp | 16 ++++++
clang/test/SemaCXX/ms-constexpr.cpp | 37 +++++++++++++
15 files changed, 245 insertions(+), 10 deletions(-)
create mode 100644 clang/test/AST/ms-constexpr.cpp
create mode 100644 clang/test/SemaCXX/ms-constexpr-invalid.cpp
create mode 100644 clang/test/SemaCXX/ms-constexpr-new.cpp
create mode 100644 clang/test/SemaCXX/ms-constexpr.cpp
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 03ed6accf700c4e6..08730e4dfd67feca 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3638,6 +3638,14 @@ def : MutualExclusions<[Owner, Pointer]>;
// Microsoft-related attributes
+def MSConstexpr : InheritableAttr {
+ let LangOpts = [MicrosoftExt];
+ let Spellings = [CXX11<"msvc", "constexpr">];
+ let Subjects = SubjectList<[Function, Stmt], ErrorDiag,
+ "functions and statements">;
+ let Documentation = [MSConstexprDocs];
+}
+
def MSNoVTable : InheritableAttr, TargetSpecificAttr<TargetMicrosoftCXXABI> {
let Spellings = [Declspec<"novtable">];
let Subjects = SubjectList<[CXXRecord]>;
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index be74535e28d8a60d..709a376bec9fbe95 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3612,6 +3612,22 @@ an error:
}];
}
+def MSConstexprDocs : Documentation {
+ let Category = DocCatStmt;
+ let Content = [{
+The ``[[msvc::constexpr]]`` attribute can be applied only to a function
+definition or a ``return`` statement. It does not impact function declarations.
+A ``[[msvc::constexpr]]`` function cannot be ``constexpr`` or ``consteval``.
+A ``[[msvc::constexpr]]`` function is treated in the same way as a ``constexpr``
+function if it is evaluated in a constant context of
+``[[msvc::constexpr]] return`` statement.
+Otherwise, it is treated as a regular function.
+
+Semantics of this attribute is enabled only under MSVC compatibility
+(``-fms-compatibility-version``) 19.33 and later.
+ }];
+}
+
def MSNoVTableDocs : Documentation {
let Category = DocCatDecl;
let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 6894e22da34e0c74..43349cdfaea75c9a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2861,8 +2861,8 @@ def warn_cxx17_compat_constexpr_local_var_no_init : Warning<
"is incompatible with C++ standards before C++20">,
InGroup<CXXPre20Compat>, DefaultIgnore;
def ext_constexpr_function_never_constant_expr : ExtWarn<
- "%select{constexpr|consteval}1 %select{function|constructor}0 never produces a "
- "constant expression">, InGroup<DiagGroup<"invalid-constexpr">>, DefaultError;
+ "%select{constexpr|consteval|[[msvc::constexpr]]}1 %select{function|constructor}0 "
+ "never produces a constant expression">, InGroup<DiagGroup<"invalid-constexpr">>, DefaultError;
def err_attr_cond_never_constant_expr : Error<
"%0 attribute expression never produces a constant expression">;
def err_diagnose_if_invalid_diagnostic_type : Error<
@@ -2884,6 +2884,12 @@ def warn_cxx11_compat_constexpr_body_multiple_return : Warning<
InGroup<CXXPre14Compat>, DefaultIgnore;
def note_constexpr_body_previous_return : Note<
"previous return statement is here">;
+def err_ms_constexpr_not_distinct : Error<
+ "[[msvc::constexpr]] cannot be applied to a %select{constexpr|consteval}0 function %1">;
+def err_ms_constexpr_virtual : Error<"virtual function cannot be [[msvc::constexpr]]">;
+def warn_ms_constexpr_no_effect : Warning<
+ "[[msvc::constexpr]] has effect only on function definitions and return statements">,
+ InGroup<IgnoredAttributes>;
// C++20 function try blocks in constexpr
def ext_constexpr_function_try_block_cxx20 : ExtWarn<
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 2d167dd2bdf1287a..c4979b9a38c0cf01 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -152,6 +152,7 @@ class LangOptions : public LangOptionsBase {
MSVC2019 = 1920,
MSVC2019_5 = 1925,
MSVC2019_8 = 1928,
+ MSVC2022_3 = 1933,
};
enum SYCLMajorVersion {
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 16697e5f076a8f8d..668484c9ed9aac88 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -641,6 +641,10 @@ namespace {
return false;
}
+ /// Whether we're in a context where [[msvc::constexpr]] evaluation is
+ /// permitted. See MSConstexprDocs for description of permitted contexts.
+ bool CanEvalMSConstexpr = false;
+
private:
APValue &createLocal(APValue::LValueBase Base, const void *Key, QualType T,
ScopeKind Scope);
@@ -674,6 +678,19 @@ namespace {
private:
llvm::TimeTraceScope TimeScope;
};
+
+ /// RAII object used to change the current ability of
+ /// [[msvc::constexpr]] evaulation.
+ struct MSConstexprContextRAII {
+ CallStackFrame &Frame;
+ bool OldValue;
+ explicit MSConstexprContextRAII(CallStackFrame &Frame, bool Value)
+ : Frame(Frame), OldValue(Frame.CanEvalMSConstexpr) {
+ Frame.CanEvalMSConstexpr = Value;
+ }
+
+ ~MSConstexprContextRAII() { Frame.CanEvalMSConstexpr = OldValue; }
+ };
}
static bool HandleDestruction(EvalInfo &Info, const Expr *E,
@@ -5544,11 +5561,14 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
case Stmt::LabelStmtClass:
return EvaluateStmt(Result, Info, cast<LabelStmt>(S)->getSubStmt(), Case);
- case Stmt::AttributedStmtClass:
- // As a general principle, C++11 attributes can be ignored without
- // any semantic impact.
- return EvaluateStmt(Result, Info, cast<AttributedStmt>(S)->getSubStmt(),
- Case);
+ case Stmt::AttributedStmtClass: {
+ const auto *AS = cast<AttributedStmt>(S);
+ const auto *SS = AS->getSubStmt();
+ MSConstexprContextRAII msConstexprContext(
+ *Info.CurrentCall, hasSpecificAttr<MSConstexprAttr>(AS->getAttrs()) &&
+ isa<ReturnStmt>(SS));
+ return EvaluateStmt(Result, Info, SS, Case);
+ }
case Stmt::CaseStmtClass:
case Stmt::DefaultStmtClass:
@@ -5619,7 +5639,9 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc,
}
// Can we evaluate this function call?
- if (Definition && Definition->isConstexpr() && Body)
+ if (Definition && Body &&
+ (Definition->isConstexpr() || Info.CurrentCall->CanEvalMSConstexpr &&
+ Definition->hasAttr<MSConstexprAttr>()))
return true;
if (Info.getLangOpts().CPlusPlus11) {
diff --git a/clang/lib/Basic/Targets/OSTargets.cpp b/clang/lib/Basic/Targets/OSTargets.cpp
index 627bc912fa231046..899aefa6173acfe1 100644
--- a/clang/lib/Basic/Targets/OSTargets.cpp
+++ b/clang/lib/Basic/Targets/OSTargets.cpp
@@ -224,6 +224,9 @@ static void addVisualCDefines(const LangOptions &Opts, MacroBuilder &Builder) {
else if (Opts.CPlusPlus14)
Builder.defineMacro("_MSVC_LANG", "201402L");
}
+
+ if (Opts.isCompatibleWithMSVC(LangOptions::MSVC2022_3))
+ Builder.defineMacro("_MSVC_CONSTEXPR_ATTRIBUTE");
}
if (Opts.MicrosoftExt) {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 23dd8ae15c16583e..0cb5d2900c4f92c9 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16190,7 +16190,9 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
ActivePolicy = &WP;
}
- if (!IsInstantiation && FD && FD->isConstexpr() && !FD->isInvalidDecl() &&
+ if (!IsInstantiation && FD &&
+ (FD->isConstexpr() || FD->hasAttr<MSConstexprAttr>()) &&
+ !FD->isInvalidDecl() &&
!CheckConstexprFunctionDefinition(FD, CheckConstexprKind::Diagnose))
FD->setInvalidDecl();
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 8966a301de1db3f2..4fd15a9d0e971485 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7342,6 +7342,27 @@ static void handleDeclspecThreadAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(::new (S.Context) ThreadAttr(S.Context, AL));
}
+static void handleMSConstexprAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ if (!S.getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2022_3)) {
+ S.Diag(AL.getLoc(), diag::warn_unknown_attribute_ignored)
+ << AL << AL.getRange();
+ return;
+ }
+ auto *FD = cast<FunctionDecl>(D);
+ if (FD->isConstexprSpecified() || FD->isConsteval()) {
+ S.Diag(AL.getLoc(), diag::err_ms_constexpr_not_distinct)
+ << FD->isConsteval() << FD;
+ return;
+ }
+ if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+ if (!S.getLangOpts().CPlusPlus20 && MD->isVirtual()) {
+ S.Diag(AL.getLoc(), diag::err_ms_constexpr_virtual);
+ return;
+ }
+ }
+ D->addAttr(::new (S.Context) MSConstexprAttr(S.Context, AL));
+}
+
static void handleAbiTagAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
SmallVector<StringRef, 4> Tags;
for (unsigned I = 0, E = AL.getNumArgs(); I != E; ++I) {
@@ -9444,6 +9465,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_Thread:
handleDeclspecThreadAttr(S, D, AL);
break;
+ case ParsedAttr::AT_MSConstexpr:
+ handleMSConstexprAttr(S, D, AL);
+ break;
// HLSL attributes:
case ParsedAttr::AT_HLSLNumThreads:
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 3688192e6cbe5c57..dadbfcc27e107ee5 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -2458,9 +2458,12 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
SmallVector<PartialDiagnosticAt, 8> Diags;
if (Kind == Sema::CheckConstexprKind::Diagnose &&
!Expr::isPotentialConstantExpr(Dcl, Diags)) {
+ int ConstKind = Dcl->hasAttr<MSConstexprAttr>() ? 2
+ : Dcl->isConsteval() ? 1
+ : 0;
SemaRef.Diag(Dcl->getLocation(),
diag::ext_constexpr_function_never_constant_expr)
- << isa<CXXConstructorDecl>(Dcl) << Dcl->isConsteval()
+ << isa<CXXConstructorDecl>(Dcl) << ConstKind
<< Dcl->getNameInfo().getSourceRange();
for (size_t I = 0, N = Diags.size(); I != N; ++I)
SemaRef.Diag(Diags[I].first, Diags[I].second);
diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index eae1eaa2f9563d53..6ce5661c45afa3db 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -397,6 +397,20 @@ static void CheckForDuplicateCodeAlignAttrs(Sema &S,
}
}
+static Attr *handleMSConstexprAttr(Sema &S, Stmt *St, const ParsedAttr &A,
+ SourceRange Range) {
+ if (!S.getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2022_3)) {
+ S.Diag(A.getLoc(), diag::warn_unknown_attribute_ignored)
+ << A << A.getRange();
+ return nullptr;
+ }
+ if (!isa<ReturnStmt>(St)) {
+ S.Diag(A.getLoc(), diag::warn_ms_constexpr_no_effect);
+ return nullptr;
+ }
+ return ::new (S.Context) MSConstexprAttr(S.Context, A);
+}
+
#define WANT_STMT_MERGE_LOGIC
#include "clang/Sema/AttrParsedAttrImpl.inc"
#undef WANT_STMT_MERGE_LOGIC
@@ -600,6 +614,8 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
return handleUnlikely(S, St, A, Range);
case ParsedAttr::AT_CodeAlign:
return handleCodeAlignAttr(S, St, A);
+ case ParsedAttr::AT_MSConstexpr:
+ return handleMSConstexprAttr(S, St, A, Range);
default:
// N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a
// declaration attribute is not written on a statement, but this code is
diff --git a/clang/test/AST/ms-constexpr.cpp b/clang/test/AST/ms-constexpr.cpp
new file mode 100644
index 0000000000000000..e85af8494f3344a0
--- /dev/null
+++ b/clang/test/AST/ms-constexpr.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++20 -ast-dump -verify %s | FileCheck %s
+// expected-no-diagnostics
+
+// CHECK: used f1 'bool ()'
+// CHECK: MSConstexprAttr 0x{{[0-9a-f]+}} <col:3, col:9>
+[[msvc::constexpr]] bool f1() { return true; }
+
+// CHECK: used constexpr f2 'bool ()'
+// CHECK-NEXT: CompoundStmt 0x{{[0-9a-f]+}} <col:21, col:56>
+// CHECK-NEXT: AttributedStmt 0x{{[0-9a-f]+}} <col:23, col:53>
+// CHECK-NEXT: MSConstexprAttr 0x{{[0-9a-f]+}} <col:25, col:31>
+// CHECK-NEXT: ReturnStmt 0x{{[0-9a-f]+}} <col:43, col:53>
+constexpr bool f2() { [[msvc::constexpr]] return f1(); }
+static_assert(f2());
+
+struct S1 {
+ // CHECK: used vm 'bool ()' virtual
+ // CHECK: MSConstexprAttr 0x{{[0-9a-f]+}} <col:7, col:13>
+ [[msvc::constexpr]] virtual bool vm() { return true; }
+
+ // CHECK: used constexpr cm 'bool ()'
+ // CHECK-NEXT: CompoundStmt 0x{{[0-9a-f]+}} <col:25, col:60>
+ // CHECK-NEXT: AttributedStmt 0x{{[0-9a-f]+}} <col:27, col:57>
+ // CHECK-NEXT: MSConstexprAttr 0x{{[0-9a-f]+}} <col:29, col:35>
+ // CHECK-NEXT: ReturnStmt 0x{{[0-9a-f]+}} <col:47, col:57>
+ constexpr bool cm() { [[msvc::constexpr]] return vm(); }
+};
+static_assert(S1{}.cm());
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index dd91f4f88ad685bd..3c46cb0645c488c2 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -89,6 +89,7 @@
// CHECK-NEXT: LoaderUninitialized (SubjectMatchRule_variable_is_global)
// CHECK-NEXT: Lockable (SubjectMatchRule_record)
// CHECK-NEXT: MIGServerRoutine (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_block)
+// CHECK-NEXT: MSConstexpr (SubjectMatchRule_function)
// CHECK-NEXT: MSStruct (SubjectMatchRule_record)
// CHECK-NEXT: MaybeUndef (SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: MicroMips (SubjectMatchRule_function)
diff --git a/clang/test/SemaCXX/ms-constexpr-invalid.cpp b/clang/test/SemaCXX/ms-constexpr-invalid.cpp
new file mode 100644
index 0000000000000000..9e88d8097e02a044
--- /dev/null
+++ b/clang/test/SemaCXX/ms-constexpr-invalid.cpp
@@ -0,0 +1,52 @@
+// 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
+
+// Check explicitly invalid code
+
+void runtime() {} // expected-note {{declared here}}
+
+[[msvc::constexpr]] void f0() { runtime(); } // expected-error {{[[msvc::constexpr]] function never produces a constant expression}} \
+ // expected-note {{non-constexpr function 'runtime' cannot be used in a constant expression}}
+[[msvc::constexpr]] constexpr void f1() {} // expected-error {{[[msvc::constexpr]] cannot be applied to a constexpr function 'f1'}}
+#if __cplusplus >= 202202L
+[[msvc::constexpr]] consteval void f2() {} // expected-error {{[[msvc::constexpr]] cannot be applied to a consteval function 'f2'}}
+#endif
+
+struct B1 {};
+struct D1 : virtual B1 { // expected-note {{virtual base class declared here}}
+ [[msvc::constexpr]] D1() {} // expected-error {{constexpr constructor not allowed in struct with virtual base class}}
+};
+
+struct [[msvc::constexpr]] S2{}; // expected-error {{'constexpr' attribute only applies to functions and statements}}
+
+// Check invalid code mixed with valid code
+
+[[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}} \
+ // expected-note {{declared here}} \
+ // expected-note {{declared here}} \
+ // expected-note {{declared here}}
+constexpr bool f5() { [[msvc::constexpr]] return f4(32) == 5; } // expected-note {{in call to 'f4(32)'}}
+static_assert(f5()); // expected-error {{static assertion expression is not an integral constant expression}} \
+ // expected-note {{in call to 'f5()'}}
+
+int f6(int x) { [[msvc::constexpr]] return x > 1 ? 1 + f6(x / 2) : 0; } // expected-note {{declared here}} \
+ // expected-note {{declared here}}
+constexpr bool f7() { [[msvc::constexpr]] return f6(32) == 5; } // expected-error {{constexpr function never produces a constant expression}} \
+ // expected-note {{non-constexpr function 'f6' cannot be used in a constant expression}} \
+ // expected-note {{non-constexpr function 'f6' cannot be used in a constant expression}}
+static_assert(f7()); // expected-error {{static assertion expression is not an integral constant expression}} \
+ // expected-note {{in call to 'f7()'}}
+
+constexpr bool f8() { // expected-error {{constexpr function never produces a constant expression}}
+ [[msvc::constexpr]] f4(32); // expected-warning {{[[msvc::constexpr]] has effect only on function definitions and return statements}} \
+ // expected-note {{non-constexpr function 'f4' cannot be used in a constant expression}} \
+ // expected-note {{non-constexpr function 'f4' cannot be used in a constant expression}}
+ [[msvc::constexpr]] int i5 = f4(32); // expected-error {{'constexpr' attribute only applies to functions and statements}}
+ return i5 == 5;
+}
+static_assert(f8()); // expected-error {{static assertion expression is not an integral constant expression}} \
+ // expected-note {{in call to 'f8()'}}
+
+#if __cplusplus == 201702L
+struct S1 { [[msvc::constexpr]] virtual bool vm() const { return true; } }; // expected-error {{virtual function cannot be [[msvc::constexpr]]}}
+#endif
diff --git a/clang/test/SemaCXX/ms-constexpr-new.cpp b/clang/test/SemaCXX/ms-constexpr-new.cpp
new file mode 100644
index 0000000000000000..05ee5c5b4e154ad6
--- /dev/null
+++ b/clang/test/SemaCXX/ms-constexpr-new.cpp
@@ -0,0 +1,16 @@
+// 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
+// supported-no-diagnostics
+
+[[nodiscard]]
+[[msvc::constexpr]] // unsupported-warning {{unknown attribute 'constexpr' ignored}}
+inline void* __cdecl operator new(decltype(sizeof(void*)), void* p) noexcept { return p; }
+
+namespace std {
+ constexpr int* construct_at(int* p, int v) {
+ [[msvc::constexpr]] return ::new (p) int(v); // unsupported-warning {{unknown attribute 'constexpr' ignored}}
+ }
+}
+
+constexpr bool check_construct_at() { int x; return *std::construct_at(&x, 42) == 42; }
+static_assert(check_construct_at());
diff --git a/clang/test/SemaCXX/ms-constexpr.cpp b/clang/test/SemaCXX/ms-constexpr.cpp
new file mode 100644
index 0000000000000000..79f71a34cb7d8485
--- /dev/null
+++ b/clang/test/SemaCXX/ms-constexpr.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++20 -verify %s
+
+[[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; }
+static_assert(test_log2());
+
+[[msvc::constexpr]] int get_value(int x)
+{
+ switch (x)
+ {
+ case 42: return 1337;
+ default:
+ if (x < 0) [[msvc::constexpr]] return log2(-x);
+ else return x;
+ }
+}
+
+constexpr bool test_complex_expr() {
+ [[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);
+}
+static_assert(test_complex_expr());
+
+constexpr bool get_constexpr_true() { return true; }
+[[msvc::constexpr]] bool get_msconstexpr_true() { return get_constexpr_true(); }
+constexpr bool test_get_msconstexpr_true() { [[msvc::constexpr]] return get_msconstexpr_true(); }
+static_assert(test_get_msconstexpr_true());
+
+// TODO (#72149): Add support for [[msvc::constexpr]] constructor; this code is valid for MSVC.
+struct S2 {
+ [[msvc::constexpr]] S2() {}
+ [[msvc::constexpr]] bool value() { return true; }
+ static constexpr bool check() { [[msvc::constexpr]] return S2{}.value(); } // expected-error {{constexpr function never produces a constant expression}} \
+ // expected-note {{non-literal type 'S2' cannot be used in a constant expression}} \
+ // expected-note {{non-literal type 'S2' cannot be used in a constant expression}}
+};
+static_assert(S2::check()); // expected-error {{static assertion expression is not an integral constant expression}} \
+ // expected-note {{in call to 'check()'}}
>From c700d21baf395aafbeea88778d13188650ed0c21 Mon Sep 17 00:00:00 2001
From: Richard Dzenis <dzenis at richard.lv>
Date: Thu, 20 Jul 2023 00:39:46 +0300
Subject: [PATCH 2/2] [clang-cl] Bump default -fms-compatibility-version to
19.33
---
clang/docs/ReleaseNotes.rst | 2 ++
clang/docs/UsersManual.rst | 4 ++--
clang/lib/Driver/ToolChains/MSVC.cpp | 4 ++--
clang/test/Driver/cl-options.c | 4 ++--
4 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0ce15169d49bcce3..e7e7c7f8702841f2 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -217,6 +217,8 @@ C23 Feature Support
Non-comprehensive list of changes in this release
-------------------------------------------------
+- The default value of `_MSC_VER` was raised from 1920 to 1933.
+ MSVC 19.33 added undocumented attribute ``[[msvc::constexpr]]``.
* Clang now has a ``__builtin_vectorelements()`` function that determines the number of elements in a vector.
For fixed-sized vectors, e.g., defined via ``__attribute__((vector_size(N)))`` or ARM NEON's vector types
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index 2e658557b0e310cd..bb37d33325889b09 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -3345,8 +3345,8 @@ default for Windows targets.
For compatibility with existing code that compiles with MSVC, clang defines the
``_MSC_VER`` and ``_MSC_FULL_VER`` macros. When on Windows, these default to
-either the same value as the currently installed version of cl.exe, or ``1920``
-and ``192000000`` (respectively). The ``-fms-compatibility-version=`` flag
+either the same value as the currently installed version of cl.exe, or ``1933``
+and ``193300000`` (respectively). The ``-fms-compatibility-version=`` flag
overrides these values. It accepts a dotted version tuple, such as 19.00.23506.
Changing the MSVC compatibility version makes clang behave more like that
version of MSVC. For example, ``-fms-compatibility-version=19`` will enable
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index 4966d102c51f1aca..9809ae39da2e4e1a 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -787,11 +787,11 @@ VersionTuple MSVCToolChain::computeMSVCVersion(const Driver *D,
if (MSVT.empty() &&
Args.hasFlag(options::OPT_fms_extensions, options::OPT_fno_ms_extensions,
IsWindowsMSVC)) {
- // -fms-compatibility-version=19.20 is default, aka 2019, 16.x
+ // -fms-compatibility-version=19.33 is default, aka 2022, 17.3
// NOTE: when changing this value, also update
// clang/docs/CommandGuide/clang.rst and clang/docs/UsersManual.rst
// accordingly.
- MSVT = VersionTuple(19, 20);
+ MSVT = VersionTuple(19, 33);
}
return MSVT;
}
diff --git a/clang/test/Driver/cl-options.c b/clang/test/Driver/cl-options.c
index 6d929b19e7e2ef8d..81d1b907eced188c 100644
--- a/clang/test/Driver/cl-options.c
+++ b/clang/test/Driver/cl-options.c
@@ -747,7 +747,7 @@
// Validate that the default triple is used when run an empty tools dir is specified
// RUN: %clang_cl -vctoolsdir "" -### -- %s 2>&1 | FileCheck %s --check-prefix VCTOOLSDIR
-// VCTOOLSDIR: "-triple" "{{[a-zA-Z0-9_-]*}}-pc-windows-msvc19.20.0"
+// VCTOOLSDIR: "-triple" "{{[a-zA-Z0-9_-]*}}-pc-windows-msvc19.33.0"
// Validate that built-in include paths are based on the supplied path
// 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 @@
// RUN: %clang_cl -vctoolsdir "" /arm64EC /c -### -- %s 2>&1 | FileCheck --check-prefix=ARM64EC %s
// ARM64EC-NOT: /arm64EC has been overridden by specified target
-// ARM64EC: "-triple" "arm64ec-pc-windows-msvc19.20.0"
+// ARM64EC: "-triple" "arm64ec-pc-windows-msvc19.33.0"
// RUN: %clang_cl -vctoolsdir "" /arm64EC /c -target x86_64-pc-windows-msvc -### -- %s 2>&1 | FileCheck --check-prefix=ARM64EC_OVERRIDE %s
// ARM64EC_OVERRIDE: warning: /arm64EC has been overridden by specified target: x86_64-pc-windows-msvc; option ignored
More information about the cfe-commits
mailing list