diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2312,9 +2312,7 @@ } /// Whether this is a (C++11) constexpr function or constexpr constructor. - bool isConstexpr() const { - return getConstexprKind() != ConstexprSpecKind::Unspecified; - } + bool isConstexpr() const; void setConstexprKind(ConstexprSpecKind CSK) { FunctionDeclBits.ConstexprKind = static_cast(CSK); } diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3500,6 +3500,13 @@ // Microsoft-related attributes +def MSConstexpr : InheritableAttr { + let LangOpts = [MicrosoftExt]; + let Spellings = [CXX11<"msvc", "constexpr">]; + let Subjects = SubjectList<[Function]>; + let Documentation = [MSConstexprDocs]; +} + def MSNoVTable : InheritableAttr, TargetSpecificAttr { let Spellings = [Declspec<"novtable">]; let Subjects = SubjectList<[CXXRecord]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3478,6 +3478,14 @@ }]; } +def MSConstexprDocs : Documentation { + let Category = DocCatStmt; + let Content = [{ +This attribute is an alias of ``constexpr`` for functions. +Available only as Microsoft extension (``-fms-extensions``). + }]; +} + def MSNoVTableDocs : Documentation { let Category = DocCatDecl; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2777,6 +2777,8 @@ InGroup, 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 'constexpr' or 'consteval' function %0">; // C++20 function try blocks in constexpr def ext_constexpr_function_try_block_cxx20 : ExtWarn< diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3121,6 +3121,10 @@ Parent->markedVirtualFunctionPure(); } +bool FunctionDecl::isConstexpr() const { + return getConstexprKind() != ConstexprSpecKind::Unspecified || hasAttr(); +} + template static bool isNamed(const NamedDecl *ND, const char (&Str)[Len]) { IdentifierInfo *II = ND->getIdentifier(); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7050,6 +7050,15 @@ D->addAttr(::new (S.Context) ThreadAttr(S.Context, AL)); } +static void handleMSConstexprAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + auto *FD = cast(D); + if (FD->isConstexprSpecified() || FD->isConsteval()) { + S.Diag(AL.getLoc(), diag::err_ms_constexpr_not_distinct) << FD; + return; + } + D->addAttr(::new (S.Context) MSConstexprAttr(S.Context, AL)); +} + static void handleAbiTagAttr(Sema &S, Decl *D, const ParsedAttr &AL) { SmallVector Tags; for (unsigned I = 0, E = AL.getNumArgs(); I != E; ++I) { @@ -8987,6 +8996,9 @@ 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/test/AST/Interp/functions.cpp b/clang/test/AST/Interp/functions.cpp --- a/clang/test/AST/Interp/functions.cpp +++ b/clang/test/AST/Interp/functions.cpp @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s // RUN: %clang_cc1 -verify=ref %s +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -fms-extensions -verify -Dconstexpr=[[msvc::constexpr]] %s +// RUN: %clang_cc1 -fms-extensions -verify=ref -Dconstexpr=[[msvc::constexpr]] %s // expected-no-diagnostics // ref-no-diagnostics diff --git a/clang/test/AST/msvc-attrs-invalid.cpp b/clang/test/AST/msvc-attrs-invalid.cpp new file mode 100644 --- /dev/null +++ b/clang/test/AST/msvc-attrs-invalid.cpp @@ -0,0 +1,14 @@ +// RUN: not %clang_cc1 -fms-extensions -std=c++20 -ast-dump %s 2> %t.stderr.txt +// RUN: FileCheck %s < %t.stderr.txt + +void runtime() {} + +// CHECK: msvc-attrs-invalid.cpp:[[@LINE+1]]:26: error: constexpr function never produces a constant expression [-Winvalid-constexpr] +[[msvc::constexpr]] void f0() { runtime(); } + +// CHECK: msvc-attrs-invalid.cpp:[[@LINE+1]]:3: error: {{\[\[}}msvc::constexpr{{]]}} cannot be applied to a 'constexpr' or 'consteval' function 'f1' +[[msvc::constexpr]] constexpr void f1() {} + +// CHECK: msvc-attrs-invalid.cpp:[[@LINE+1]]:3: error: {{\[\[}}msvc::constexpr{{]]}} cannot be applied to a 'constexpr' or 'consteval' function 'f2' +[[msvc::constexpr]] consteval void f2() {} + diff --git a/clang/test/AST/msvc-attrs.cpp b/clang/test/AST/msvc-attrs.cpp new file mode 100644 --- /dev/null +++ b/clang/test/AST/msvc-attrs.cpp @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -fms-extensions -std=c++20 -ast-dump %s | FileCheck %s +// RUN: not %clang_cc1 -Werror=ignored-attributes -ast-dump %s 2> %t.stderr.txt +// RUN: FileCheck -check-prefix CHECK-DIAG-NO-MSX %s < %t.stderr.txt + +// CHECK: msvc-attrs.cpp:[[@LINE+3]]:21, col:32> col:26 f0 'void ()' +// CHECK: MSConstexprAttr 0x{{[0-9a-f]+}} +// CHECK-DIAG-NO-MSX: msvc-attrs.cpp:[[@LINE+1]]:3: error: 'constexpr' attribute ignored +[[msvc::constexpr]] void f0() {} + +struct s0 { + // CHECK: CXXConstructorDecl 0x{{[0-9a-f]+}} col:23 s0 'void ()' implicit-inline + // CHECK: MSConstexprAttr 0x{{[0-9a-f]+}} + [[msvc::constexpr]] s0() {} + + // CHECK: CXXDestructorDecl 0x{{[0-9a-f]+}} col:23 ~s0 'void () noexcept' implicit-inline + // CHECK: MSConstexprAttr 0x{{[0-9a-f]+}} + [[msvc::constexpr]] ~s0() {} + + // CHECK: CXXMethodDecl 0x{{[0-9a-f]+}} col:36 used f1 'void ()' virtual implicit-inline + // CHECK: MSConstexprAttr 0x{{[0-9a-f]+}} + [[msvc::constexpr]] virtual void f1() {} +}; + +// Check 'constexpr' and '[[msvc::constexpr]]' functions can call each other +void f2(); +constexpr void f3(); + +[[msvc::constexpr]] void f2() { f3(); } +constexpr void f3() { f2(); } + +[[msvc::constexpr]] void f4(); +constexpr void f5(); + +void f4() { f5(); } +constexpr void f5() { f4(); } diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -83,6 +83,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)