RIscRIpt updated this revision to Diff 540803.
RIscRIpt marked an inline comment as done.
RIscRIpt added a comment.

Bump

Renamed clang/test/AST/msvc-* to ms-*
Tried to improve doc note.
Rebased onto one of stable commits from main.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D134475/new/

https://reviews.llvm.org/D134475

Files:
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/DiagnosticASTKinds.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/AST/ExprConstant.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/lib/Sema/SemaStmtAttr.cpp
  clang/test/AST/ms-attrs-invalid.cpp
  clang/test/AST/ms-attrs.cpp
  clang/test/AST/ms-constexpr-new.cpp
  clang/test/Misc/pragma-attribute-supported-attributes-list.test

Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -84,6 +84,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)
Index: clang/test/AST/ms-constexpr-new.cpp
===================================================================
--- /dev/null
+++ clang/test/AST/ms-constexpr-new.cpp
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -fms-extensions -std=c++20 -ast-dump %s | FileCheck %s
+
+// CHECK: used operator new
+// CHECK: MSConstexprAttr 0x{{[0-9a-f]+}} <col:17, col:23>
+[[nodiscard]] [[msvc::constexpr]] inline void* __cdecl operator new(decltype(sizeof(void*)), void* p) noexcept { return p; }
+
+// CHECK: used constexpr construct_at
+// CHECK: AttributedStmt 0x{{[0-9a-f]+}} <col:46, col:88>
+// CHECK-NEXT: MSConstexprAttr 0x{{[0-9a-f]+}} <col:48, col:54>
+// CHECK-NEXT: ReturnStmt 0x{{[0-9a-f]+}} <col:66, col:88>
+constexpr int* construct_at(int* p, int v) { [[msvc::constexpr]] return ::new (p) int(v); }
+
+constexpr bool check_construct_at() { int x; return *construct_at(&x, 42) == 42; }
+
+static_assert(check_construct_at());
Index: clang/test/AST/ms-attrs.cpp
===================================================================
--- /dev/null
+++ clang/test/AST/ms-attrs.cpp
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -fms-extensions -std=c++20 -ast-dump %s | FileCheck %s
+
+// [[msvc::constexpr]] tests
+
+// MSConstexprDocs (1)
+// CHECK: used f1 'bool ()'
+// CHECK: MSConstexprAttr 0x{{[0-9a-f]+}} <col:3, col:9>
+[[msvc::constexpr]] bool f1() { return true; }
+
+// MSConstexprDocs (2)
+// CHECK: used constexpr f2 'bool ()'
+// CHECK: 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());
+
+constexpr bool f3() { [[msvc::constexpr]] return f1() || f1() && f1(); }
+static_assert(f3());
+
+[[msvc::constexpr]] int f4(int x) { [[msvc::constexpr]] return x > 1 ? 1 + f4(x / 2) : 0; }
+constexpr bool f5() { [[msvc::constexpr]] return f4(32) == 5; }
+static_assert(f5());
+
+[[msvc::constexpr]] int f6(int x)
+{
+    switch (x)
+    {
+        case 42: return 1337;
+        default:
+                 if (x < 0) [[msvc::constexpr]] return f4(-x);
+                 else return x;
+    }
+}
+
+constexpr bool f7() { [[msvc::constexpr]] return f6(f6(42) - 1337 + f6(-32) - 5 + (f6(1) ? f6(0) : f6(2))) == f6(0); }
+static_assert(f7());
+
+constexpr bool f8() { return true; }
+[[msvc::constexpr]] bool f9() { return f8(); }
+constexpr bool f10() { [[msvc::constexpr]] return f9(); }
+static_assert(f10());
+
+struct S1 {
+    [[msvc::constexpr]] virtual bool vm() { return true; }
+    constexpr bool cm() { [[msvc::constexpr]] return vm(); }
+};
+static_assert(S1{}.cm());
+
+/*
+// TODO: Add support for [[msvc::constexpr]] constructor
+struct S2 {
+    [[msvc::constexpr]] S2() {}
+    [[msvc::constexpr]] bool value() { return true; }
+    static constexpr bool check() { [[msvc::constexpr]] return S2{}.value(); }
+};
+static_assert(S2::check());
+*/
Index: clang/test/AST/ms-attrs-invalid.cpp
===================================================================
--- /dev/null
+++ clang/test/AST/ms-attrs-invalid.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -fms-extensions -std=c++20 -verify %s
+// RUN: %clang_cc1 -fms-extensions -std=c++17 -verify=cpp17 %s
+
+#if __cplusplus >= 202002L
+// 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'}}
+[[msvc::constexpr]] consteval void f2() {} // expected-error {{[[msvc::constexpr]] cannot be applied to a consteval function 'f2'}}
+
+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 {{[[msvc::constexpr]] function 'f4' is used not in '[[msvc::constexpr]] return' statement}}
+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 {{[[msvc::constexpr]] function 'f4' is used not in '[[msvc::constexpr]] return' statement}} \
+                                // expected-note {{[[msvc::constexpr]] function 'f4' is used not in '[[msvc::constexpr]] return' statement}}
+    [[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()'}}
+#else
+
+struct S1 {
+    [[msvc::constexpr]] virtual bool vm() const { return true; } // cpp17-error {{virtual function cannot be [[msvc::constexpr]]}}
+};
+
+#endif
Index: clang/lib/Sema/SemaStmtAttr.cpp
===================================================================
--- clang/lib/Sema/SemaStmtAttr.cpp
+++ clang/lib/Sema/SemaStmtAttr.cpp
@@ -322,6 +322,13 @@
   return ::new (S.Context) UnlikelyAttr(S.Context, A);
 }
 
+static Attr *handleMSConstexprAttr(Sema &S, Stmt *St, const ParsedAttr &A,
+                                   SourceRange Range) {
+  if (!isa<ReturnStmt>(St))
+    S.Diag(A.getLoc(), diag::warn_ms_constexpr_no_effect);
+  return ::new (S.Context) MSConstexprAttr(S.Context, A);
+}
+
 #define WANT_STMT_MERGE_LOGIC
 #include "clang/Sema/AttrParsedAttrImpl.inc"
 #undef WANT_STMT_MERGE_LOGIC
@@ -523,6 +530,8 @@
     return handleLikely(S, St, A, Range);
   case ParsedAttr::AT_Unlikely:
     return handleUnlikely(S, St, A, Range);
+  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
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -2425,9 +2425,12 @@
   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;
     for (size_t I = 0, N = Diags.size(); I != N; ++I)
       SemaRef.Diag(Diags[I].first, Diags[I].second);
     // Don't return false here: we allow this for compatibility in
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -7316,6 +7316,22 @@
   D->addAttr(::new (S.Context) ThreadAttr(S.Context, AL));
 }
 
+static void handleMSConstexprAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  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) {
@@ -9286,6 +9302,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:
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -15909,7 +15909,9 @@
         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();
 
Index: clang/lib/AST/ExprConstant.cpp
===================================================================
--- clang/lib/AST/ExprConstant.cpp
+++ clang/lib/AST/ExprConstant.cpp
@@ -639,6 +639,10 @@
       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);
@@ -672,6 +676,19 @@
   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,
@@ -5506,11 +5523,16 @@
   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,
+        Info.getLangOpts().MicrosoftExt &&
+            hasSpecificAttr<MSConstexprAttr>(AS->getAttrs()) &&
+            isa<ReturnStmt>(SS));
+    return EvaluateStmt(Result, Info, SS, Case);
+  }
 
   case Stmt::CaseStmtClass:
   case Stmt::DefaultStmtClass:
@@ -5581,8 +5603,29 @@
   }
 
   // Can we evaluate this function call?
-  if (Definition && Definition->isConstexpr() && Body)
-    return true;
+  if (Definition && Body) {
+    if (Definition->isConstexpr())
+      return true;
+
+    bool canEvalMSConstexpr = Info.CurrentCall->CanEvalMSConstexpr;
+    bool isMSConstexpr = Definition->hasAttr<MSConstexprAttr>();
+    if (canEvalMSConstexpr && isMSConstexpr)
+      return true;
+
+    if (canEvalMSConstexpr || isMSConstexpr) {
+      // Diagnose invalid usage of [[msvc::constexpr]] function
+      bool isConstructor = isa<CXXConstructorDecl>(Definition);
+      if (canEvalMSConstexpr) { // !isMSConstexpr
+        Info.FFDiag(CallLoc, diag::note_constexpr_invalid_function, 1)
+            << /*IsConstexpr*/ 0 << isConstructor << Definition;
+        Info.Note(Definition->getLocation(), diag::note_declared_at);
+      } else if (isMSConstexpr) { // !canEvalMSConstexpr
+        Info.FFDiag(CallLoc, diag::note_ms_constexpr_invalid_context)
+            << isConstructor << Definition;
+      }
+      return false;
+    }
+  }
 
   if (Info.getLangOpts().CPlusPlus11) {
     const FunctionDecl *DiagDecl = Definition ? Definition : Declaration;
@@ -9545,8 +9588,10 @@
 
   bool IsNothrow = false;
   bool IsPlacement = false;
+  bool IsMSConstexpr = Info.CurrentCall->CanEvalMSConstexpr &&
+                       OperatorNew->hasAttr<MSConstexprAttr>();
   if (OperatorNew->isReservedGlobalPlacementOperator() &&
-      Info.CurrentCall->isStdFunction() && !E->isArray()) {
+      (Info.CurrentCall->isStdFunction() || IsMSConstexpr) && !E->isArray()) {
     // FIXME Support array placement new.
     assert(E->getNumPlacementArgs() == 1);
     if (!EvaluatePointer(E->getPlacementArg(0), Result, Info))
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2786,8 +2786,8 @@
   "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<
@@ -2809,6 +2809,12 @@
   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<
Index: clang/include/clang/Basic/DiagnosticASTKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticASTKinds.td
+++ clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -389,6 +389,9 @@
   "type %0 has unexpected layout">;
 def note_constexpr_unsupported_flexible_array : Note<
   "flexible array initialization is not yet supported">;
+def note_ms_constexpr_invalid_context : Note<
+  "[[msvc::constexpr]] %select{function|constructor}0 %1 is used not in "
+  "'[[msvc::constexpr]] return' statement">;
 def err_experimental_clang_interp_failed : Error<
   "the experimental clang interpreter failed to evaluate an expression">;
 
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -3593,6 +3593,25 @@
 }];
 }
 
+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.
+
+This attribute is available only as a Microsoft extension (``-fms-extensions``).
+
+.. Note:: To use a ``[[msvc::constexpr]]`` function in a constant context,
+   one may want to create a ``constexpr`` function-wrapper and invoke the
+   ``[[msvc::constexpr]]`` function within a ``[[msvc::constexpr]] return`` statement.
+  }];
+}
+
 def MSNoVTableDocs : Documentation {
   let Category = DocCatDecl;
   let Content = [{
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -3530,6 +3530,14 @@
 
 // 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]>;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D134475: [Clang] Ad... Richard Dzenis via Phabricator via cfe-commits

Reply via email to