https://github.com/eleviant updated 
https://github.com/llvm/llvm-project/pull/197005

>From a57015a0753e896414993a2b0edebbc72bbcec8d Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Mon, 11 May 2026 19:39:23 +0200
Subject: [PATCH 01/14] [clang] Allow C-style casts in constexpr in MS
 compatible mode

Patch allows folding constant expression if -fms-compatibility is given
and the only problem found by evaluator is C-style cast. This makes it
more permissive than MSVC, which treats this expression as constant:
```
(FIELD_OFFSET(S,y) + 3) % 5
```
but doesn't do the same for this one:
```
(FIELD_OFFSET(S,y) + 3)
```
where FIELD_OFFSET is defined as:
```
```
---
 clang/include/clang/AST/ASTContext.h       |   3 +
 clang/lib/AST/ASTContext.cpp               |   6 +
 clang/lib/AST/Decl.cpp                     |   8 +-
 clang/lib/Sema/SemaExpr.cpp                |   2 +
 clang/lib/Sema/SemaOverload.cpp            |   3 +-
 clang/test/SemaCXX/microsoft-constexpr.cpp | 142 +++++++++++++++++++++
 6 files changed, 161 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/SemaCXX/microsoft-constexpr.cpp

diff --git a/clang/include/clang/AST/ASTContext.h 
b/clang/include/clang/AST/ASTContext.h
index c952b8d46974c..3f9d57284752a 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3884,6 +3884,9 @@ OPT_LIST(V)
   void recordMemberDataPointerEvaluation(const ValueDecl *VD);
   void recordOffsetOfEvaluation(const OffsetOfExpr *E);
 
+  bool
+  shouldIgnoreNotesForConstEval(SmallVectorImpl<PartialDiagnosticAt> &Notes);
+
 private:
   /// All OMPTraitInfo objects live in this collection, one per
   /// `pragma omp [begin] declare variant` directive.
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index a401a7471e6fc..8cf00713c2a72 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15633,3 +15633,9 @@ void ASTContext::recordOffsetOfEvaluation(const 
OffsetOfExpr *E) {
   if (FieldDecl *FD = Comp.getField(); isPFPField(FD))
     PFPFieldsWithEvaluatedOffset.insert(FD);
 }
+
+bool ASTContext::shouldIgnoreNotesForConstEval(
+    SmallVectorImpl<PartialDiagnosticAt> &Notes) {
+  return getLangOpts().MSVCCompat && Notes.size() == 1 &&
+         Notes[0].second.getDiagID() == diag::note_constexpr_invalid_cast;
+}
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index b797ebfa1a7e1..d0876834c913b 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2587,8 +2587,12 @@ APValue 
*VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
   if (IsConstantInitialization &&
       (Ctx.getLangOpts().CPlusPlus ||
        (isConstexpr() && Ctx.getLangOpts().C23)) &&
-      !Notes.empty())
-    Result = false;
+      !Notes.empty()) {
+    if (!Ctx.shouldIgnoreNotesForConstEval(Notes))
+      Result = false;
+    else
+      Notes.clear();
+  }
 
   // Ensure the computed APValue is cleaned up later if evaluation succeeded,
   // or that it's empty (so that there's nothing to clean up) if evaluation
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index ad6e7183cb3a4..0769bc787e886 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18030,6 +18030,8 @@ Sema::VerifyIntegerConstantExpression(Expr *E, 
llvm::APSInt *Result,
   // In C++11, we can rely on diagnostics being produced for any expression
   // which is not a constant expression. If no diagnostics were produced, then
   // this is a constant expression.
+  if (getASTContext().shouldIgnoreNotesForConstEval(Notes))
+    Notes.clear();
   if (Folded && getLangOpts().CPlusPlus11 && Notes.empty()) {
     if (Result)
       *Result = EvalResult.Val.getInt();
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index e11bbd7085798..768bd294d7b69 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6710,7 +6710,8 @@ Sema::EvaluateConvertedConstantExpression(Expr *E, 
QualType T, APValue &Value,
     Result = ExprError();
   } else {
     Value = Eval.Val;
-
+    if (getASTContext().shouldIgnoreNotesForConstEval(Notes))
+      Notes.clear();
     if (Notes.empty()) {
       // It's a constant expression.
       Expr *E = Result.get();
diff --git a/clang/test/SemaCXX/microsoft-constexpr.cpp 
b/clang/test/SemaCXX/microsoft-constexpr.cpp
new file mode 100644
index 0000000000000..fb0a849e5ce7b
--- /dev/null
+++ b/clang/test/SemaCXX/microsoft-constexpr.cpp
@@ -0,0 +1,142 @@
+// Some of this should fail in MSVC, but work in clang
+// when -fms-compatibility is enabled.
+// RUN: %clang -fsyntax-only -fms-compatibility -std=c++20 %s
+
+typedef long LONG;
+typedef __int64 LONG_PTR, *PLONG_PTR;
+
+#define FIELD_OFFSET(type, field) ((LONG_PTR)&(((type *)0)->field))
+
+struct S {
+  int x;
+  int y;
+};
+
+constexpr bool cb_eq  = FIELD_OFFSET(S, y) == 4;
+constexpr bool cb_ne  = FIELD_OFFSET(S, y) != 0;
+constexpr bool cb_lt  = FIELD_OFFSET(S, y) < 8;
+constexpr bool cb_le  = FIELD_OFFSET(S, y) <= 4;
+constexpr bool cb_gt  = FIELD_OFFSET(S, y) > 0;
+constexpr bool cb_ge  = FIELD_OFFSET(S, y) >= 4;
+constexpr bool cb_bool = FIELD_OFFSET(S, y);
+
+static_assert(FIELD_OFFSET(S, y) == 4);
+static_assert(FIELD_OFFSET(S, y) != 0);
+static_assert(FIELD_OFFSET(S, y) < 8);
+static_assert(FIELD_OFFSET(S, y) <= 4);
+static_assert(FIELD_OFFSET(S, y) > 0);
+static_assert(FIELD_OFFSET(S, y) >= 4);
+static_assert(FIELD_OFFSET(S, y));
+
+
+enum E {
+  enum_offset_y = FIELD_OFFSET(S, y),
+  enum_cmp_y    = FIELD_OFFSET(S, y) == 4
+};
+
+int arr_bound[FIELD_OFFSET(S, y)];
+int arr_bound_cmp[FIELD_OFFSET(S, y) == 4 ? 1 : -1];
+
+struct BitField {
+  int bf1 : FIELD_OFFSET(S, y);
+  int bf2 : FIELD_OFFSET(S, y) == 4;
+};
+
+template<int N>
+struct TplInt {};
+
+template<bool B>
+struct TplBool {};
+
+TplInt<FIELD_OFFSET(S, y)> tpl_int;
+TplBool<FIELD_OFFSET(S, y) == 4> tpl_bool;
+TplBool<FIELD_OFFSET(S, y)> tpl_bool_conv;
+
+void f() noexcept(FIELD_OFFSET(S, y) == 4) {}
+
+template<class T>
+void g() {
+  if constexpr (FIELD_OFFSET(S, y) == 4) {
+  } else {
+  }
+}
+
+struct ExplicitCtor {
+  explicit(FIELD_OFFSET(S, y) == 4) ExplicitCtor(int) {}
+};
+
+alignas(FIELD_OFFSET(S,y)) int __g;
+
+constinit int constinit_offset = FIELD_OFFSET(S, y);
+constinit bool constinit_bool = FIELD_OFFSET(S, y) == 4;
+
+constexpr int constexpr_offset = FIELD_OFFSET(S, y);
+constexpr int constexpr_cmp_as_int = FIELD_OFFSET(S, y) == 4;
+constexpr bool constexpr_bool = FIELD_OFFSET(S, y) == 4;
+
+int switch_test(int v) {
+  switch (v) {
+  case FIELD_OFFSET(S, y):
+    return 1;
+  case FIELD_OFFSET(S, x):
+    return 2;
+  default:
+    return 0;
+  }
+}
+
+template<int N = FIELD_OFFSET(S, y)>
+struct DefaultTplInt {};
+
+template<bool B = FIELD_OFFSET(S, y) == 4>
+struct DefaultTplBool {};
+
+DefaultTplInt<> default_tpl_int;
+DefaultTplBool<> default_tpl_bool;
+
+struct ArrayMember {
+  int a[FIELD_OFFSET(S, y)];
+};
+
+union U {
+  char c;
+  int a[FIELD_OFFSET(S, y)];
+};
+
+typedef char typedef_arr[FIELD_OFFSET(S, y)];
+using using_arr = char[FIELD_OFFSET(S, y)];
+
+constexpr int ternary_offset =
+    FIELD_OFFSET(S, y) == 4 ? FIELD_OFFSET(S, y) : -1;
+
+constexpr bool logical_and =
+    FIELD_OFFSET(S, y) == 4 && FIELD_OFFSET(S, x) == 0;
+
+constexpr bool logical_or =
+    FIELD_OFFSET(S, y) == 4 || FIELD_OFFSET(S, x) == 123;
+
+constexpr bool logical_not =
+    !FIELD_OFFSET(S, x);
+
+constexpr int arithmetic_add = FIELD_OFFSET(S, y) + 1;
+constexpr int arithmetic_sub = FIELD_OFFSET(S, y) - 1;
+constexpr int arithmetic_mul = FIELD_OFFSET(S, y) * 2;
+constexpr int arithmetic_div = FIELD_OFFSET(S, y) / 2;
+constexpr int arithmetic_mod = FIELD_OFFSET(S, y) % 3;
+
+constexpr int bit_or  = FIELD_OFFSET(S, y) | 1;
+constexpr int bit_and = FIELD_OFFSET(S, y) & 7;
+constexpr int bit_xor = FIELD_OFFSET(S, y) ^ 1;
+constexpr int bit_shl = FIELD_OFFSET(S, y) << 1;
+constexpr int bit_shr = FIELD_OFFSET(S, y) >> 1;
+
+constexpr int comma_expr = (0, FIELD_OFFSET(S, y));
+
+constexpr int cast_int = (int)FIELD_OFFSET(S, y);
+constexpr long cast_long = (long)FIELD_OFFSET(S, y);
+constexpr bool cast_bool = (bool)FIELD_OFFSET(S, y);
+
+template<class T, int N>
+struct DependentTpl {};
+
+DependentTpl<S, FIELD_OFFSET(S, y)> dependent_tpl;

>From 875536c0e1d81260c7fc83d2cb48a6f5ada26a47 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Tue, 12 May 2026 16:52:07 +0200
Subject: [PATCH 02/14] Add SFINAE handling and test cases

We don't want relaxed constant folding to happen during template
parameter subsititution, to avoid unexpected instantiations.
---
 clang/lib/Sema/SemaExpr.cpp                   |  8 ++++++--
 clang/lib/Sema/SemaOverload.cpp               |  5 ++++-
 .../SemaCXX/microsoft-constexpr-SFINAE.cpp    | 20 +++++++++++++++++++
 .../SemaCXX/microsoft-constexpr-SFINAE2.cpp   | 20 +++++++++++++++++++
 4 files changed, 50 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/SemaCXX/microsoft-constexpr-SFINAE.cpp
 create mode 100644 clang/test/SemaCXX/microsoft-constexpr-SFINAE2.cpp

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 0769bc787e886..342ab881f0282 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18027,11 +18027,15 @@ Sema::VerifyIntegerConstantExpression(Expr *E, 
llvm::APSInt *Result,
   if (!isa<ConstantExpr>(E))
     E = ConstantExpr::Create(Context, E, EvalResult.Val);
 
+  // For -fms-compatibility mode we relax some requirements
+  // for constant folding in non-SFINAE contexts
+  if (!isSFINAEContext() &&
+      getASTContext().shouldIgnoreNotesForConstEval(Notes))
+    Notes.clear();
+
   // In C++11, we can rely on diagnostics being produced for any expression
   // which is not a constant expression. If no diagnostics were produced, then
   // this is a constant expression.
-  if (getASTContext().shouldIgnoreNotesForConstEval(Notes))
-    Notes.clear();
   if (Folded && getLangOpts().CPlusPlus11 && Notes.empty()) {
     if (Result)
       *Result = EvalResult.Val.getInt();
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 768bd294d7b69..e71eebfd8f40e 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6710,7 +6710,10 @@ Sema::EvaluateConvertedConstantExpression(Expr *E, 
QualType T, APValue &Value,
     Result = ExprError();
   } else {
     Value = Eval.Val;
-    if (getASTContext().shouldIgnoreNotesForConstEval(Notes))
+    // For -fms-compatibility mode we relax some requirements
+    // for constant folding in non-SFINAE contexts
+    if (!isSFINAEContext() &&
+        getASTContext().shouldIgnoreNotesForConstEval(Notes))
       Notes.clear();
     if (Notes.empty()) {
       // It's a constant expression.
diff --git a/clang/test/SemaCXX/microsoft-constexpr-SFINAE.cpp 
b/clang/test/SemaCXX/microsoft-constexpr-SFINAE.cpp
new file mode 100644
index 0000000000000..271d1bcfad99e
--- /dev/null
+++ b/clang/test/SemaCXX/microsoft-constexpr-SFINAE.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -fms-compatibility -triple 
x86_64-windows-msvc %s
+
+typedef long long LONG_PTR;
+typedef long LONG;
+#define FIELD_OFFSET(type, field) ((LONG_PTR)&(((type *)0)->field))
+
+struct S {
+  int x;
+  int y;
+};
+
+template<class T, LONG_PTR = FIELD_OFFSET(S, y)>
+char probe(int);
+
+template<class>
+long probe(...);
+
+static_assert(sizeof(probe<int>(0)) == sizeof(char), "");
+// expected-error@-1 {{static assertion failed due to requirement 'sizeof 
(probe<int>(0)) == sizeof(char)'}}
+// expected-note@-2 {{expression evaluates to '4 == 1'}}
diff --git a/clang/test/SemaCXX/microsoft-constexpr-SFINAE2.cpp 
b/clang/test/SemaCXX/microsoft-constexpr-SFINAE2.cpp
new file mode 100644
index 0000000000000..b63ea73eaab28
--- /dev/null
+++ b/clang/test/SemaCXX/microsoft-constexpr-SFINAE2.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -fsyntax-only -fms-compatibility -triple 
x86_64-windows-msvc -verify %s
+
+typedef long long LONG_PTR;
+typedef long LONG;
+#define FIELD_OFFSET(type, field) ((LONG_PTR)&(((type *)0)->field))
+
+struct S {
+  int x;
+  int y;
+};
+
+template<class T, bool = __builtin_choose_expr(FIELD_OFFSET(T, y) > 0, true, 
false)>
+char probe(int);
+
+template<class>
+long probe(...);
+
+static_assert(sizeof(probe<S>(0)) == sizeof(char), "");
+// expected-error@-1 {{static assertion failed due to requirement 'sizeof 
(probe<S>(0)) == sizeof(char)'}}
+// expected-note@-2 {{expression evaluates to '4 == 1'}}

>From 2337ddc90e5ee019c21c540b562ec281cdd49bb6 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Tue, 12 May 2026 19:22:43 +0200
Subject: [PATCH 03/14] Add opt-in warning for MS relaxed constant folding

---
 clang/include/clang/AST/ASTContext.h            |  3 +--
 clang/include/clang/Basic/DiagnosticASTKinds.td |  4 ++++
 clang/include/clang/Basic/DiagnosticGroups.td   |  2 ++
 clang/lib/AST/ASTContext.cpp                    | 11 ++++++++---
 clang/lib/AST/Decl.cpp                          |  4 +---
 clang/lib/Sema/SemaExpr.cpp                     |  5 ++---
 clang/lib/Sema/SemaOverload.cpp                 |  5 ++---
 clang/test/SemaCXX/microsoft-constexpr2.cpp     | 12 ++++++++++++
 8 files changed, 32 insertions(+), 14 deletions(-)
 create mode 100644 clang/test/SemaCXX/microsoft-constexpr2.cpp

diff --git a/clang/include/clang/AST/ASTContext.h 
b/clang/include/clang/AST/ASTContext.h
index 3f9d57284752a..0387cef5b6a4d 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3884,8 +3884,7 @@ OPT_LIST(V)
   void recordMemberDataPointerEvaluation(const ValueDecl *VD);
   void recordOffsetOfEvaluation(const OffsetOfExpr *E);
 
-  bool
-  shouldIgnoreNotesForConstEval(SmallVectorImpl<PartialDiagnosticAt> &Notes);
+  bool maybeFoldMSConstexpr(SmallVectorImpl<PartialDiagnosticAt> &Notes);
 
 private:
   /// All OMPTraitInfo objects live in this collection, one per
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td 
b/clang/include/clang/Basic/DiagnosticASTKinds.td
index bde418695f647..b7252902c969e 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -1030,6 +1030,10 @@ def warn_npot_ms_struct : Warning<
   "ms_struct may not produce Microsoft-compatible layouts with fundamental "
   "data types with sizes that aren't a power of two">,
   DefaultError, InGroup<IncompatibleMSStruct>;
+def warn_relaxed_constant_fold : Warning<
+  "folding this constant expression is a Microsoft extension">,
+  InGroup<MicrosoftRelaxedConstantFold>, DefaultIgnore;
+
 
 def err_itanium_layout_unimplemented : Error<
   "Itanium-compatible layout for the Microsoft C++ ABI is not yet supported">;
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index 8031f99419bdc..fe2eadd7a3aa4 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1642,6 +1642,8 @@ def MicrosoftStringLiteralFromPredefined : DiagGroup<
     "microsoft-string-literal-from-predefined">;
 def MicrosoftInlineOnNonFunction : DiagGroup<
     "microsoft-inline-on-non-function">;
+def MicrosoftRelaxedConstantFold :
+  DiagGroup<"relaxed-constant-fold">;
 
 // Aliases.
 def : DiagGroup<"msvc-include", [MicrosoftInclude]>;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 8cf00713c2a72..956e000a927c9 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15634,8 +15634,13 @@ void ASTContext::recordOffsetOfEvaluation(const 
OffsetOfExpr *E) {
     PFPFieldsWithEvaluatedOffset.insert(FD);
 }
 
-bool ASTContext::shouldIgnoreNotesForConstEval(
+bool ASTContext::maybeFoldMSConstexpr(
     SmallVectorImpl<PartialDiagnosticAt> &Notes) {
-  return getLangOpts().MSVCCompat && Notes.size() == 1 &&
-         Notes[0].second.getDiagID() == diag::note_constexpr_invalid_cast;
+  bool Fold = getLangOpts().MSVCCompat && Notes.size() == 1 &&
+              Notes[0].second.getDiagID() == diag::note_constexpr_invalid_cast;
+  if (Fold) {
+    getDiagnostics().Report(Notes[0].first, diag::warn_relaxed_constant_fold);
+    Notes.clear();
+  }
+  return Fold;
 }
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index d0876834c913b..987167342e49d 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2588,10 +2588,8 @@ APValue 
*VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
       (Ctx.getLangOpts().CPlusPlus ||
        (isConstexpr() && Ctx.getLangOpts().C23)) &&
       !Notes.empty()) {
-    if (!Ctx.shouldIgnoreNotesForConstEval(Notes))
+    if (!Ctx.maybeFoldMSConstexpr(Notes))
       Result = false;
-    else
-      Notes.clear();
   }
 
   // Ensure the computed APValue is cleaned up later if evaluation succeeded,
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 342ab881f0282..f39a9e56e6535 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18029,9 +18029,8 @@ Sema::VerifyIntegerConstantExpression(Expr *E, 
llvm::APSInt *Result,
 
   // For -fms-compatibility mode we relax some requirements
   // for constant folding in non-SFINAE contexts
-  if (!isSFINAEContext() &&
-      getASTContext().shouldIgnoreNotesForConstEval(Notes))
-    Notes.clear();
+  if (!isSFINAEContext())
+    getASTContext().maybeFoldMSConstexpr(Notes);
 
   // In C++11, we can rely on diagnostics being produced for any expression
   // which is not a constant expression. If no diagnostics were produced, then
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index e71eebfd8f40e..cf1dbea6c2379 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6712,9 +6712,8 @@ Sema::EvaluateConvertedConstantExpression(Expr *E, 
QualType T, APValue &Value,
     Value = Eval.Val;
     // For -fms-compatibility mode we relax some requirements
     // for constant folding in non-SFINAE contexts
-    if (!isSFINAEContext() &&
-        getASTContext().shouldIgnoreNotesForConstEval(Notes))
-      Notes.clear();
+    if (!isSFINAEContext())
+      getASTContext().maybeFoldMSConstexpr(Notes);
     if (Notes.empty()) {
       // It's a constant expression.
       Expr *E = Result.get();
diff --git a/clang/test/SemaCXX/microsoft-constexpr2.cpp 
b/clang/test/SemaCXX/microsoft-constexpr2.cpp
new file mode 100644
index 0000000000000..25d4b3ed2b3f3
--- /dev/null
+++ b/clang/test/SemaCXX/microsoft-constexpr2.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -fms-compatibility 
-Wrelaxed-constant-fold %s
+
+typedef long long LONG_PTR;
+typedef long LONG;
+#define FIELD_OFFSET(type, field) ((LONG_PTR)&(((type *)0)->field))
+
+struct S {
+  int x;
+  int y;
+};
+
+constexpr long b = FIELD_OFFSET(S, y); // expected-warning {{folding this 
constant expression is a Microsoft extension}}

>From a524d313d5ce09e7c67cc8b6237a3e82f86b712c Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Thu, 14 May 2026 17:00:59 +0200
Subject: [PATCH 04/14] Change definition from Warning to Extension

---
 clang/include/clang/Basic/DiagnosticASTKinds.td | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td 
b/clang/include/clang/Basic/DiagnosticASTKinds.td
index b7252902c969e..192eb85b019cf 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -1030,9 +1030,9 @@ def warn_npot_ms_struct : Warning<
   "ms_struct may not produce Microsoft-compatible layouts with fundamental "
   "data types with sizes that aren't a power of two">,
   DefaultError, InGroup<IncompatibleMSStruct>;
-def warn_relaxed_constant_fold : Warning<
+def warn_relaxed_constant_fold : Extension<
   "folding this constant expression is a Microsoft extension">,
-  InGroup<MicrosoftRelaxedConstantFold>, DefaultIgnore;
+  InGroup<MicrosoftRelaxedConstantFold>;
 
 
 def err_itanium_layout_unimplemented : Error<

>From 03359733c30849c248787df0ae3e051c153a243e Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Thu, 14 May 2026 17:07:02 +0200
Subject: [PATCH 05/14] Don't allow folding constant expressions using
 dynamic_cast

This can be done with -std=c++20, so we don't need to this in Microsoft
compatibility mode.
---
 clang/include/clang/Basic/PartialDiagnostic.h |  8 ++++++++
 clang/lib/AST/ASTContext.cpp                  |  7 +++++--
 clang/test/SemaCXX/microsoft-constexpr3.cpp   | 18 ++++++++++++++++++
 3 files changed, 31 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/SemaCXX/microsoft-constexpr3.cpp

diff --git a/clang/include/clang/Basic/PartialDiagnostic.h 
b/clang/include/clang/Basic/PartialDiagnostic.h
index 4bf6049d08fdb..7658bfa3795f4 100644
--- a/clang/include/clang/Basic/PartialDiagnostic.h
+++ b/clang/include/clang/Basic/PartialDiagnostic.h
@@ -189,6 +189,14 @@ class PartialDiagnostic : public StreamingDiagnostic {
              == DiagnosticsEngine::ak_std_string && "Not a string arg");
     return DiagStorage->DiagArgumentsStr[I];
   }
+  uint64_t getValueArg(unsigned I) {
+    assert(DiagStorage && "No diagnostic storage?");
+    assert(I < DiagStorage->NumDiagArgs && "Not enough diagnostic args");
+    assert(DiagStorage->DiagArgumentsKind[I] !=
+               DiagnosticsEngine::ak_std_string &&
+           "Not an integer arg");
+    return DiagStorage->DiagArgumentsVal[I];
+  }
 };
 
 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 956e000a927c9..15099a3814e5c 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15636,8 +15636,11 @@ void ASTContext::recordOffsetOfEvaluation(const 
OffsetOfExpr *E) {
 
 bool ASTContext::maybeFoldMSConstexpr(
     SmallVectorImpl<PartialDiagnosticAt> &Notes) {
-  bool Fold = getLangOpts().MSVCCompat && Notes.size() == 1 &&
-              Notes[0].second.getDiagID() == diag::note_constexpr_invalid_cast;
+  bool Fold =
+      getLangOpts().MSVCCompat && Notes.size() == 1 &&
+      Notes[0].second.getDiagID() == diag::note_constexpr_invalid_cast &&
+      Notes[0].second.getValueArg(0) != 
diag::ConstexprInvalidCastKind::Dynamic;
+
   if (Fold) {
     getDiagnostics().Report(Notes[0].first, diag::warn_relaxed_constant_fold);
     Notes.clear();
diff --git a/clang/test/SemaCXX/microsoft-constexpr3.cpp 
b/clang/test/SemaCXX/microsoft-constexpr3.cpp
new file mode 100644
index 0000000000000..dd52bb8f81d32
--- /dev/null
+++ b/clang/test/SemaCXX/microsoft-constexpr3.cpp
@@ -0,0 +1,18 @@
+// Ignore dynamic_cast when relaxing constant expression with 
-fms-compatibility
+// However using dynamic_cast is still possible in c++20 and higher
+// RUN: not %clang_cc1 -std=c++11 -fms-compatibility -fsyntax-only %s
+// RUN: %clang_cc1 -std=c++20 -fms-compatibility -fsyntax-only %s
+
+struct B {
+  virtual ~B() {}
+};
+
+struct D : B {
+  int x = 123;
+};
+
+#define IsD(x) (dynamic_cast<const D*>(x) != 0)
+
+static const D od;
+
+constexpr bool is_d = IsD(&od);

>From 2263d9f615579c9f3638e8f91c6b9ab5e127c51c Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Thu, 14 May 2026 20:02:28 +0200
Subject: [PATCH 06/14] Improved diagnostics

---
 .../include/clang/Basic/DiagnosticASTKinds.td |  5 ++--
 clang/include/clang/Basic/PartialDiagnostic.h |  2 +-
 clang/lib/AST/ASTContext.cpp                  | 30 +++++++++++++------
 clang/test/SemaCXX/microsoft-constexpr2.cpp   |  4 ++-
 4 files changed, 28 insertions(+), 13 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td 
b/clang/include/clang/Basic/DiagnosticASTKinds.td
index 192eb85b019cf..7f0f0a503a8ba 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -1031,10 +1031,11 @@ def warn_npot_ms_struct : Warning<
   "data types with sizes that aren't a power of two">,
   DefaultError, InGroup<IncompatibleMSStruct>;
 def warn_relaxed_constant_fold : Extension<
-  "folding this constant expression is a Microsoft extension">,
+  "folding constant expression involving "
+  "%select{reinterpret_cast|cast that performs the conversions of a 
reinterpret_cast}0"
+  " is a Microsoft extension">,
   InGroup<MicrosoftRelaxedConstantFold>;
 
-
 def err_itanium_layout_unimplemented : Error<
   "Itanium-compatible layout for the Microsoft C++ ABI is not yet supported">;
 
diff --git a/clang/include/clang/Basic/PartialDiagnostic.h 
b/clang/include/clang/Basic/PartialDiagnostic.h
index 7658bfa3795f4..7469e45f7d888 100644
--- a/clang/include/clang/Basic/PartialDiagnostic.h
+++ b/clang/include/clang/Basic/PartialDiagnostic.h
@@ -194,7 +194,7 @@ class PartialDiagnostic : public StreamingDiagnostic {
     assert(I < DiagStorage->NumDiagArgs && "Not enough diagnostic args");
     assert(DiagStorage->DiagArgumentsKind[I] !=
                DiagnosticsEngine::ak_std_string &&
-           "Not an integer arg");
+           "Not a value arg");
     return DiagStorage->DiagArgumentsVal[I];
   }
 };
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 15099a3814e5c..38f292987722f 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15636,14 +15636,26 @@ void ASTContext::recordOffsetOfEvaluation(const 
OffsetOfExpr *E) {
 
 bool ASTContext::maybeFoldMSConstexpr(
     SmallVectorImpl<PartialDiagnosticAt> &Notes) {
-  bool Fold =
-      getLangOpts().MSVCCompat && Notes.size() == 1 &&
-      Notes[0].second.getDiagID() == diag::note_constexpr_invalid_cast &&
-      Notes[0].second.getValueArg(0) != 
diag::ConstexprInvalidCastKind::Dynamic;
-
-  if (Fold) {
-    getDiagnostics().Report(Notes[0].first, diag::warn_relaxed_constant_fold);
-    Notes.clear();
+  if (Notes.size() != 1 || !getLangOpts().MSVCCompat)
+    return false;
+  auto &PD = Notes[0].second;
+  if (PD.getDiagID() != diag::note_constexpr_invalid_cast)
+    return false;
+  unsigned CastID;
+  switch (PD.getValueArg(0)) {
+  case diag::ConstexprInvalidCastKind::Reinterpret:
+    CastID = 0;
+    break;
+  case diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret:
+    if (!PD.getValueArg(1))
+      return false;
+    CastID = 1;
+    break;
+  default:
+    return false;
   }
-  return Fold;
+  getDiagnostics().Report(Notes[0].first, diag::warn_relaxed_constant_fold)
+      << CastID;
+  Notes.clear();
+  return true;
 }
diff --git a/clang/test/SemaCXX/microsoft-constexpr2.cpp 
b/clang/test/SemaCXX/microsoft-constexpr2.cpp
index 25d4b3ed2b3f3..e756a0240d5fe 100644
--- a/clang/test/SemaCXX/microsoft-constexpr2.cpp
+++ b/clang/test/SemaCXX/microsoft-constexpr2.cpp
@@ -3,10 +3,12 @@
 typedef long long LONG_PTR;
 typedef long LONG;
 #define FIELD_OFFSET(type, field) ((LONG_PTR)&(((type *)0)->field))
+#define FIELD_OFFSET2(type, field) (reinterpret_cast<LONG_PTR>(&(((type 
*)0)->field)))
 
 struct S {
   int x;
   int y;
 };
 
-constexpr long b = FIELD_OFFSET(S, y); // expected-warning {{folding this 
constant expression is a Microsoft extension}}
+constexpr long b = FIELD_OFFSET(S, y); // expected-warning {{folding constant 
expression involving cast that performs the conversions of a reinterpret_cast 
is a Microsoft extension}}
+constexpr long b2 = FIELD_OFFSET2(S, y); // expected-warning {{folding 
constant expression involving reinterpret_cast is a Microsoft extension}}

>From 8b83a3dedfb359471ffeb1589c1c327d3ea79fe9 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Mon, 18 May 2026 13:50:18 +0200
Subject: [PATCH 07/14] Add note to distinguish ptr to int conversions

---
 .../include/clang/Basic/DiagnosticASTKinds.td  |  4 ++++
 clang/lib/AST/ASTContext.cpp                   | 18 +++---------------
 clang/lib/AST/ExprConstant.cpp                 |  4 ++--
 3 files changed, 9 insertions(+), 17 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td 
b/clang/include/clang/Basic/DiagnosticASTKinds.td
index 7f0f0a503a8ba..ce864bdb1fe8a 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -16,6 +16,10 @@ def note_constexpr_invalid_cast : Note<
   "of a reinterpret_cast}1}|%CastFrom{cast from %1}}0"
   " is not allowed in a constant expression"
   "%select{| in C++ standards before C++20||}0">;
+def note_constexpr_invalid_cast_ptrtoint : Note<
+  "%select{reinterpret_cast||"
+  "%select{this conversion|cast that performs the conversions of a 
reinterpret_cast}1|"
+  "}0 is not allowed in a constant expression">;
 def note_constexpr_invalid_void_star_cast : Note<
   "cast from %0 is not allowed in a constant expression "
   "%select{in C++ standards before C++2c|because the pointed object "
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 38f292987722f..55effa4e2397e 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15639,23 +15639,11 @@ bool ASTContext::maybeFoldMSConstexpr(
   if (Notes.size() != 1 || !getLangOpts().MSVCCompat)
     return false;
   auto &PD = Notes[0].second;
-  if (PD.getDiagID() != diag::note_constexpr_invalid_cast)
+  if (PD.getDiagID() != diag::note_constexpr_invalid_cast_ptrtoint)
     return false;
-  unsigned CastID;
-  switch (PD.getValueArg(0)) {
-  case diag::ConstexprInvalidCastKind::Reinterpret:
-    CastID = 0;
-    break;
-  case diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret:
-    if (!PD.getValueArg(1))
-      return false;
-    CastID = 1;
-    break;
-  default:
-    return false;
-  }
   getDiagnostics().Report(Notes[0].first, diag::warn_relaxed_constant_fold)
-      << CastID;
+      << !!PD.getValueArg(0);
   Notes.clear();
+
   return true;
 }
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index b1cea63a8ede6..75d268df7bf10 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -8533,7 +8533,7 @@ class ExprEvaluatorBase
   }
 
   bool VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr *E) {
-    CCEDiag(E, diag::note_constexpr_invalid_cast)
+    CCEDiag(E, diag::note_constexpr_invalid_cast_ptrtoint)
         << diag::ConstexprInvalidCastKind::Reinterpret;
     return static_cast<Derived*>(this)->VisitCastExpr(E);
   }
@@ -19484,7 +19484,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) 
{
   }
 
   case CK_PointerToIntegral: {
-    CCEDiag(E, diag::note_constexpr_invalid_cast)
+    CCEDiag(E, diag::note_constexpr_invalid_cast_ptrtoint)
         << diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
         << Info.Ctx.getLangOpts().CPlusPlus << E->getSourceRange();
 

>From 361c530c42e5db9a453868541d7e6549243be230 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Tue, 19 May 2026 14:49:52 +0200
Subject: [PATCH 08/14] Don't allow LValue casts for constexpr

---
 clang/include/clang/AST/ASTContext.h        | 3 ++-
 clang/lib/AST/ASTContext.cpp                | 4 ++--
 clang/lib/AST/Decl.cpp                      | 2 +-
 clang/lib/Sema/SemaExpr.cpp                 | 2 +-
 clang/lib/Sema/SemaOverload.cpp             | 2 +-
 clang/test/SemaCXX/microsoft-constexpr2.cpp | 4 +++-
 6 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h 
b/clang/include/clang/AST/ASTContext.h
index 0387cef5b6a4d..8a1f55350b0ca 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3884,7 +3884,8 @@ OPT_LIST(V)
   void recordMemberDataPointerEvaluation(const ValueDecl *VD);
   void recordOffsetOfEvaluation(const OffsetOfExpr *E);
 
-  bool maybeFoldMSConstexpr(SmallVectorImpl<PartialDiagnosticAt> &Notes);
+  bool maybeFoldMSConstexpr(APValue &Val,
+                            SmallVectorImpl<PartialDiagnosticAt> &Notes);
 
 private:
   /// All OMPTraitInfo objects live in this collection, one per
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 55effa4e2397e..01a14f0ac2cb9 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15635,8 +15635,8 @@ void ASTContext::recordOffsetOfEvaluation(const 
OffsetOfExpr *E) {
 }
 
 bool ASTContext::maybeFoldMSConstexpr(
-    SmallVectorImpl<PartialDiagnosticAt> &Notes) {
-  if (Notes.size() != 1 || !getLangOpts().MSVCCompat)
+    APValue &Val, SmallVectorImpl<PartialDiagnosticAt> &Notes) {
+  if (Notes.size() != 1 || !getLangOpts().MSVCCompat || Val.isLValue())
     return false;
   auto &PD = Notes[0].second;
   if (PD.getDiagID() != diag::note_constexpr_invalid_cast_ptrtoint)
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 987167342e49d..c96982d83c1d7 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2588,7 +2588,7 @@ APValue 
*VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
       (Ctx.getLangOpts().CPlusPlus ||
        (isConstexpr() && Ctx.getLangOpts().C23)) &&
       !Notes.empty()) {
-    if (!Ctx.maybeFoldMSConstexpr(Notes))
+    if (!Ctx.maybeFoldMSConstexpr(Eval->Evaluated, Notes))
       Result = false;
   }
 
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index f39a9e56e6535..b114d30977600 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18030,7 +18030,7 @@ Sema::VerifyIntegerConstantExpression(Expr *E, 
llvm::APSInt *Result,
   // For -fms-compatibility mode we relax some requirements
   // for constant folding in non-SFINAE contexts
   if (!isSFINAEContext())
-    getASTContext().maybeFoldMSConstexpr(Notes);
+    getASTContext().maybeFoldMSConstexpr(EvalResult.Val, Notes);
 
   // In C++11, we can rely on diagnostics being produced for any expression
   // which is not a constant expression. If no diagnostics were produced, then
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index cf1dbea6c2379..575add96a51ea 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6713,7 +6713,7 @@ Sema::EvaluateConvertedConstantExpression(Expr *E, 
QualType T, APValue &Value,
     // For -fms-compatibility mode we relax some requirements
     // for constant folding in non-SFINAE contexts
     if (!isSFINAEContext())
-      getASTContext().maybeFoldMSConstexpr(Notes);
+      getASTContext().maybeFoldMSConstexpr(Value, Notes);
     if (Notes.empty()) {
       // It's a constant expression.
       Expr *E = Result.get();
diff --git a/clang/test/SemaCXX/microsoft-constexpr2.cpp 
b/clang/test/SemaCXX/microsoft-constexpr2.cpp
index e756a0240d5fe..ad4faddc83b1d 100644
--- a/clang/test/SemaCXX/microsoft-constexpr2.cpp
+++ b/clang/test/SemaCXX/microsoft-constexpr2.cpp
@@ -8,7 +8,9 @@ typedef long LONG;
 struct S {
   int x;
   int y;
-};
+} ob;
 
 constexpr long b = FIELD_OFFSET(S, y); // expected-warning {{folding constant 
expression involving cast that performs the conversions of a reinterpret_cast 
is a Microsoft extension}}
 constexpr long b2 = FIELD_OFFSET2(S, y); // expected-warning {{folding 
constant expression involving reinterpret_cast is a Microsoft extension}}
+constexpr LONG_PTR b3 = (LONG_PTR)&ob; // expected-error {{constexpr variable 
'b3' must be initialized by a constant expression}}
+                                      // expected-note@-1 {{cast that performs 
the conversions of a reinterpret_cast is not allowed in a constant expression}}

>From a5c2ebb979f9191fc09166d422b3933db3955546 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Mon, 25 May 2026 11:46:54 +0200
Subject: [PATCH 09/14] Rename a function

The name MSConstexpr is already taken by class handling
[[msvc::constexpr]] attribute
---
 clang/include/clang/AST/ASTContext.h | 4 ++--
 clang/lib/AST/ASTContext.cpp         | 2 +-
 clang/lib/AST/Decl.cpp               | 2 +-
 clang/lib/Sema/SemaExpr.cpp          | 2 +-
 clang/lib/Sema/SemaOverload.cpp      | 2 +-
 5 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h 
b/clang/include/clang/AST/ASTContext.h
index 8a1f55350b0ca..71a167103d991 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3884,8 +3884,8 @@ OPT_LIST(V)
   void recordMemberDataPointerEvaluation(const ValueDecl *VD);
   void recordOffsetOfEvaluation(const OffsetOfExpr *E);
 
-  bool maybeFoldMSConstexpr(APValue &Val,
-                            SmallVectorImpl<PartialDiagnosticAt> &Notes);
+  bool maybeFoldConstexprWithCast(APValue &Val,
+                                  SmallVectorImpl<PartialDiagnosticAt> &Notes);
 
 private:
   /// All OMPTraitInfo objects live in this collection, one per
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 01a14f0ac2cb9..3eea213e0a2d5 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15634,7 +15634,7 @@ void ASTContext::recordOffsetOfEvaluation(const 
OffsetOfExpr *E) {
     PFPFieldsWithEvaluatedOffset.insert(FD);
 }
 
-bool ASTContext::maybeFoldMSConstexpr(
+bool ASTContext::maybeFoldConstexprWithCast(
     APValue &Val, SmallVectorImpl<PartialDiagnosticAt> &Notes) {
   if (Notes.size() != 1 || !getLangOpts().MSVCCompat || Val.isLValue())
     return false;
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index c96982d83c1d7..ad33b1d30fc07 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2588,7 +2588,7 @@ APValue 
*VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
       (Ctx.getLangOpts().CPlusPlus ||
        (isConstexpr() && Ctx.getLangOpts().C23)) &&
       !Notes.empty()) {
-    if (!Ctx.maybeFoldMSConstexpr(Eval->Evaluated, Notes))
+    if (!Ctx.maybeFoldConstexprWithCast(Eval->Evaluated, Notes))
       Result = false;
   }
 
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index b114d30977600..7cb17fb9bf5b4 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18030,7 +18030,7 @@ Sema::VerifyIntegerConstantExpression(Expr *E, 
llvm::APSInt *Result,
   // For -fms-compatibility mode we relax some requirements
   // for constant folding in non-SFINAE contexts
   if (!isSFINAEContext())
-    getASTContext().maybeFoldMSConstexpr(EvalResult.Val, Notes);
+    getASTContext().maybeFoldConstexprWithCast(EvalResult.Val, Notes);
 
   // In C++11, we can rely on diagnostics being produced for any expression
   // which is not a constant expression. If no diagnostics were produced, then
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 575add96a51ea..bcb9b427ee123 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6713,7 +6713,7 @@ Sema::EvaluateConvertedConstantExpression(Expr *E, 
QualType T, APValue &Value,
     // For -fms-compatibility mode we relax some requirements
     // for constant folding in non-SFINAE contexts
     if (!isSFINAEContext())
-      getASTContext().maybeFoldMSConstexpr(Value, Notes);
+      getASTContext().maybeFoldConstexprWithCast(Value, Notes);
     if (Notes.empty()) {
       // It's a constant expression.
       Expr *E = Result.get();

>From b369e5c18540c6c2c3ca5949e529489534da64c7 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Mon, 25 May 2026 16:03:26 +0200
Subject: [PATCH 10/14] Fix bugs in evaluator

- Correctly emit note, when handling reinterpret_cast
- Don't try to attempt folding constexpr having reinterpret_cast,
  if evaluator has failed (APValue is None).
---
 clang/lib/AST/Decl.cpp                      | 2 +-
 clang/lib/AST/ExprConstant.cpp              | 4 +++-
 clang/test/SemaCXX/microsoft-constexpr2.cpp | 6 ++++++
 3 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index ad33b1d30fc07..65b1cac985f6f 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2584,7 +2584,7 @@ APValue 
*VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
   // a constant initializer if we produced notes. In that case, we can't keep
   // the result, because it may only be correct under the assumption that the
   // initializer is a constant context.
-  if (IsConstantInitialization &&
+  if (Result && IsConstantInitialization &&
       (Ctx.getLangOpts().CPlusPlus ||
        (isConstexpr() && Ctx.getLangOpts().C23)) &&
       !Notes.empty()) {
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 75d268df7bf10..5326a8b3d4880 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -8533,7 +8533,9 @@ class ExprEvaluatorBase
   }
 
   bool VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr *E) {
-    CCEDiag(E, diag::note_constexpr_invalid_cast_ptrtoint)
+    bool IsPtrToInt = E->getCastKind() == CK_PointerToIntegral;
+    CCEDiag(E, IsPtrToInt ? diag::note_constexpr_invalid_cast_ptrtoint
+                          : diag::note_constexpr_invalid_cast)
         << diag::ConstexprInvalidCastKind::Reinterpret;
     return static_cast<Derived*>(this)->VisitCastExpr(E);
   }
diff --git a/clang/test/SemaCXX/microsoft-constexpr2.cpp 
b/clang/test/SemaCXX/microsoft-constexpr2.cpp
index ad4faddc83b1d..c28e0f3d17574 100644
--- a/clang/test/SemaCXX/microsoft-constexpr2.cpp
+++ b/clang/test/SemaCXX/microsoft-constexpr2.cpp
@@ -14,3 +14,9 @@ constexpr long b = FIELD_OFFSET(S, y); // expected-warning 
{{folding constant ex
 constexpr long b2 = FIELD_OFFSET2(S, y); // expected-warning {{folding 
constant expression involving reinterpret_cast is a Microsoft extension}}
 constexpr LONG_PTR b3 = (LONG_PTR)&ob; // expected-error {{constexpr variable 
'b3' must be initialized by a constant expression}}
                                       // expected-note@-1 {{cast that performs 
the conversions of a reinterpret_cast is not allowed in a constant expression}}
+constexpr int* b4 = reinterpret_cast<int*>(&ob); // expected-error {{constexpr 
variable 'b4' must be initialized by a constant expression}}
+                                                // expected-note@-1 
{{reinterpret_cast is not allowed in a constant expression}}
+constexpr LONG_PTR b5 = (42 - FIELD_OFFSET(S, y)) +       // expected-error 
{{constexpr variable 'b5' must be initialized by a constant expression}}
+                (8 + reinterpret_cast<LONG_PTR>(&ob));    // expected-note@-1 
{{reinterpret_cast is not allowed in a constant expression}}
+constexpr LONG_PTR b6 = -reinterpret_cast<LONG_PTR>(&ob); // expected-error 
{{constexpr variable 'b6' must be initialized by a constant expression}}
+                                                         // expected-note@-1 
{{reinterpret_cast is not allowed in a constant expression}}

>From 78cbaa116620ba07ecb798adfe8f14f8ddab7a56 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Tue, 26 May 2026 15:20:17 +0200
Subject: [PATCH 11/14] Don't allow constexprs having a cast to contain an
 l-value

---
 clang/include/clang/AST/ASTContext.h        |  4 ++--
 clang/include/clang/AST/Expr.h              |  4 ++++
 clang/lib/AST/ASTContext.cpp                |  4 ++--
 clang/lib/AST/Decl.cpp                      |  5 ++---
 clang/lib/AST/ExprConstant.cpp              | 10 +++++++---
 clang/lib/Sema/SemaExpr.cpp                 |  4 ++--
 clang/lib/Sema/SemaOverload.cpp             |  4 ++--
 clang/test/SemaCXX/microsoft-constexpr2.cpp |  2 ++
 8 files changed, 23 insertions(+), 14 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h 
b/clang/include/clang/AST/ASTContext.h
index 71a167103d991..884ec336090fc 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3884,8 +3884,8 @@ OPT_LIST(V)
   void recordMemberDataPointerEvaluation(const ValueDecl *VD);
   void recordOffsetOfEvaluation(const OffsetOfExpr *E);
 
-  bool maybeFoldConstexprWithCast(APValue &Val,
-                                  SmallVectorImpl<PartialDiagnosticAt> &Notes);
+  bool
+  maybeFoldConstexprWithCast(SmallVectorImpl<PartialDiagnosticAt> &Notes) 
const;
 
 private:
   /// All OMPTraitInfo objects live in this collection, one per
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index b91bf4a5375fb..d786a8964b7f8 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -619,6 +619,10 @@ class Expr : public ValueStmt {
     /// Likewise, INT_MAX + 1 can be folded to INT_MIN, but has UB.
     bool HasUndefinedBehavior = false;
 
+    /// Whether part of expression is an LValue.
+    /// Used when evaluating constant expression with Microsoft extensions.
+    bool HasLValue = false;
+
     /// Diag - If this is non-null, it will be filled in with a stack of notes
     /// indicating why evaluation failed (or why it failed to produce a 
constant
     /// expression).
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 3eea213e0a2d5..dc0fdea473645 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15635,8 +15635,8 @@ void ASTContext::recordOffsetOfEvaluation(const 
OffsetOfExpr *E) {
 }
 
 bool ASTContext::maybeFoldConstexprWithCast(
-    APValue &Val, SmallVectorImpl<PartialDiagnosticAt> &Notes) {
-  if (Notes.size() != 1 || !getLangOpts().MSVCCompat || Val.isLValue())
+    SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
+  if (Notes.size() != 1 || !getLangOpts().MSVCCompat)
     return false;
   auto &PD = Notes[0].second;
   if (PD.getDiagID() != diag::note_constexpr_invalid_cast_ptrtoint)
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 65b1cac985f6f..d880924e2d6ab 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2584,12 +2584,11 @@ APValue 
*VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
   // a constant initializer if we produced notes. In that case, we can't keep
   // the result, because it may only be correct under the assumption that the
   // initializer is a constant context.
-  if (Result && IsConstantInitialization &&
+  if (IsConstantInitialization &&
       (Ctx.getLangOpts().CPlusPlus ||
        (isConstexpr() && Ctx.getLangOpts().C23)) &&
       !Notes.empty()) {
-    if (!Ctx.maybeFoldConstexprWithCast(Eval->Evaluated, Notes))
-      Result = false;
+    Result = false;
   }
 
   // Ensure the computed APValue is cleaned up later if evaluation succeeded,
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 5326a8b3d4880..0f8e2c6b45266 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -19495,6 +19495,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) 
{
       return false;
 
     if (LV.getLValueBase()) {
+      Info.EvalStatus.HasLValue = true;
       // Only allow based lvalue casts if they are lossless.
       // FIXME: Allow a larger integer size than the pointer size, and allow
       // narrowing back down to pointer width in subsequent integral casts.
@@ -21543,9 +21544,12 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const 
ASTContext &Ctx,
       llvm_unreachable("Unhandled cleanup; missing full expression marker?");
   }
 
-  return CheckConstantExpression(Info, DeclLoc, DeclTy, Value,
-                                 ConstantExprKind::Normal) &&
-         CheckMemoryLeaks(Info);
+  bool Checked = CheckConstantExpression(Info, DeclLoc, DeclTy, Value,
+                                         ConstantExprKind::Normal) &&
+                 CheckMemoryLeaks(Info);
+  if (Checked && !Info.EvalStatus.HasLValue)
+    Ctx.maybeFoldConstexprWithCast(Notes);
+  return Checked;
 }
 
 bool VarDecl::evaluateDestruction(
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 7cb17fb9bf5b4..35951d66c58d2 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18029,8 +18029,8 @@ Sema::VerifyIntegerConstantExpression(Expr *E, 
llvm::APSInt *Result,
 
   // For -fms-compatibility mode we relax some requirements
   // for constant folding in non-SFINAE contexts
-  if (!isSFINAEContext())
-    getASTContext().maybeFoldConstexprWithCast(EvalResult.Val, Notes);
+  if (!isSFINAEContext() && !EvalResult.HasLValue)
+    getASTContext().maybeFoldConstexprWithCast(Notes);
 
   // In C++11, we can rely on diagnostics being produced for any expression
   // which is not a constant expression. If no diagnostics were produced, then
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index bcb9b427ee123..f4bf26ee85fb6 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6712,8 +6712,8 @@ Sema::EvaluateConvertedConstantExpression(Expr *E, 
QualType T, APValue &Value,
     Value = Eval.Val;
     // For -fms-compatibility mode we relax some requirements
     // for constant folding in non-SFINAE contexts
-    if (!isSFINAEContext())
-      getASTContext().maybeFoldConstexprWithCast(Value, Notes);
+    if (!isSFINAEContext() && !Eval.HasLValue)
+      getASTContext().maybeFoldConstexprWithCast(Notes);
     if (Notes.empty()) {
       // It's a constant expression.
       Expr *E = Result.get();
diff --git a/clang/test/SemaCXX/microsoft-constexpr2.cpp 
b/clang/test/SemaCXX/microsoft-constexpr2.cpp
index c28e0f3d17574..6aff24aa36a44 100644
--- a/clang/test/SemaCXX/microsoft-constexpr2.cpp
+++ b/clang/test/SemaCXX/microsoft-constexpr2.cpp
@@ -20,3 +20,5 @@ constexpr LONG_PTR b5 = (42 - FIELD_OFFSET(S, y)) +       // 
expected-error {{co
                 (8 + reinterpret_cast<LONG_PTR>(&ob));    // expected-note@-1 
{{reinterpret_cast is not allowed in a constant expression}}
 constexpr LONG_PTR b6 = -reinterpret_cast<LONG_PTR>(&ob); // expected-error 
{{constexpr variable 'b6' must be initialized by a constant expression}}
                                                          // expected-note@-1 
{{reinterpret_cast is not allowed in a constant expression}}
+constexpr long b7[2] = { FIELD_OFFSET(S, y), (long)&ob }; // expected-error 
{{constexpr variable 'b7' must be initialized by a constant expression}}
+                                                         // expected-note@-1 
{{cast that performs the conversions of a reinterpret_cast is not allowed in a 
constant expression}}

>From c7a98611a1d88464ccbdfd51b0caf733332aeb0e Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Tue, 26 May 2026 21:35:51 +0200
Subject: [PATCH 12/14] Fix failing test on Windows

---
 clang/test/SemaCXX/microsoft-constexpr2.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/test/SemaCXX/microsoft-constexpr2.cpp 
b/clang/test/SemaCXX/microsoft-constexpr2.cpp
index 6aff24aa36a44..fd83b08ce9a3d 100644
--- a/clang/test/SemaCXX/microsoft-constexpr2.cpp
+++ b/clang/test/SemaCXX/microsoft-constexpr2.cpp
@@ -20,5 +20,5 @@ constexpr LONG_PTR b5 = (42 - FIELD_OFFSET(S, y)) +       // 
expected-error {{co
                 (8 + reinterpret_cast<LONG_PTR>(&ob));    // expected-note@-1 
{{reinterpret_cast is not allowed in a constant expression}}
 constexpr LONG_PTR b6 = -reinterpret_cast<LONG_PTR>(&ob); // expected-error 
{{constexpr variable 'b6' must be initialized by a constant expression}}
                                                          // expected-note@-1 
{{reinterpret_cast is not allowed in a constant expression}}
-constexpr long b7[2] = { FIELD_OFFSET(S, y), (long)&ob }; // expected-error 
{{constexpr variable 'b7' must be initialized by a constant expression}}
-                                                         // expected-note@-1 
{{cast that performs the conversions of a reinterpret_cast is not allowed in a 
constant expression}}
+constexpr LONG_PTR b7[2] = { FIELD_OFFSET(S, y), (LONG_PTR)&ob }; // 
expected-error {{constexpr variable 'b7' must be initialized by a constant 
expression}}
+                                                                 // 
expected-note@-1 {{cast that performs the conversions of a reinterpret_cast is 
not allowed in a constant expression}}

>From 835fede090c43ad58077cdc5197ea9b87f4e7992 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Mon, 1 Jun 2026 14:41:39 +0200
Subject: [PATCH 13/14] Limit notes passed during constexpr evaluation

This patch permits only note_constexpr_invalid_cast_ptrtoint
followed by note_constexpr_null_subobject to be ignored by the
constexpr evaluator in -fms-compatibility mode. This restricts
the range of allowed expressions to offsetof-like cases only.
---
 clang/lib/AST/ByteCode/State.cpp            | 18 ++++++++++++++++++
 clang/lib/AST/ByteCode/State.h              |  2 ++
 clang/test/SemaCXX/microsoft-constexpr2.cpp |  2 ++
 3 files changed, 22 insertions(+)

diff --git a/clang/lib/AST/ByteCode/State.cpp b/clang/lib/AST/ByteCode/State.cpp
index 00e3b1a331172..75d8fc4fefb1a 100644
--- a/clang/lib/AST/ByteCode/State.cpp
+++ b/clang/lib/AST/ByteCode/State.cpp
@@ -18,6 +18,23 @@ using namespace clang::interp;
 
 State::~State() {}
 
+// With -fms-compatibility we allow pointer to integer casts
+// followed by nullptr casts.
+void State::clearDiagIfNeeded(diag::kind DiagId) {
+  switch (DiagId) {
+  case diag::note_constexpr_invalid_cast_ptrtoint:
+  case diag::note_constexpr_null_subobject:
+    return;
+  }
+
+  auto *Diag = EvalStatus.Diag;
+  if (!Ctx.getLangOpts().MSVCCompat || !Diag || Diag->size() != 1 ||
+      (*Diag)[0].second.getDiagID() !=
+          diag::note_constexpr_invalid_cast_ptrtoint)
+    return;
+  Diag->clear();
+}
+
 OptionalDiagnostic State::FFDiag(SourceLocation Loc, diag::kind DiagId,
                                  unsigned ExtraNotes) {
   return diag(Loc, DiagId, ExtraNotes, false);
@@ -41,6 +58,7 @@ OptionalDiagnostic State::FFDiag(SourceInfo SI, diag::kind 
DiagId,
 
 OptionalDiagnostic State::CCEDiag(SourceLocation Loc, diag::kind DiagId,
                                   unsigned ExtraNotes) {
+  clearDiagIfNeeded(DiagId);
   // Don't override a previous diagnostic. Don't bother collecting
   // diagnostics if we're evaluating for overflow.
   if (!EvalStatus.Diag || !EvalStatus.Diag->empty()) {
diff --git a/clang/lib/AST/ByteCode/State.h b/clang/lib/AST/ByteCode/State.h
index a720033c7914b..289c024f417e3 100644
--- a/clang/lib/AST/ByteCode/State.h
+++ b/clang/lib/AST/ByteCode/State.h
@@ -93,6 +93,8 @@ class State {
   ASTContext &getASTContext() const { return Ctx; }
   const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); }
 
+  void clearDiagIfNeeded(diag::kind DiagId);
+
   /// Note that we have had a side-effect, and determine whether we should
   /// keep evaluating.
   bool noteSideEffect() const {
diff --git a/clang/test/SemaCXX/microsoft-constexpr2.cpp 
b/clang/test/SemaCXX/microsoft-constexpr2.cpp
index fd83b08ce9a3d..1b917e635d66f 100644
--- a/clang/test/SemaCXX/microsoft-constexpr2.cpp
+++ b/clang/test/SemaCXX/microsoft-constexpr2.cpp
@@ -22,3 +22,5 @@ constexpr LONG_PTR b6 = -reinterpret_cast<LONG_PTR>(&ob); // 
expected-error {{co
                                                          // expected-note@-1 
{{reinterpret_cast is not allowed in a constant expression}}
 constexpr LONG_PTR b7[2] = { FIELD_OFFSET(S, y), (LONG_PTR)&ob }; // 
expected-error {{constexpr variable 'b7' must be initialized by a constant 
expression}}
                                                                  // 
expected-note@-1 {{cast that performs the conversions of a reinterpret_cast is 
not allowed in a constant expression}}
+constexpr LONG_PTR b8  = (LONG_PTR)((char*)1 + FIELD_OFFSET(S, y)); // 
expected-error {{constexpr variable 'b8' must be initialized by a constant 
expression}}
+                                                                   // 
expected-note@-1 {{cast that performs the conversions of a reinterpret_cast is 
not allowed in a constant expression}}

>From 7d02030ac9dba3658a9dc807f7c044328b825010 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Wed, 3 Jun 2026 10:00:08 +0200
Subject: [PATCH 14/14] Refactor and add comments

---
 clang/lib/AST/ASTContext.cpp     | 10 ++++++++--
 clang/lib/AST/ByteCode/State.cpp | 19 ++++++++++++++++---
 2 files changed, 24 insertions(+), 5 deletions(-)

diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index dc0fdea473645..c574942f00d5a 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15634,9 +15634,16 @@ void ASTContext::recordOffsetOfEvaluation(const 
OffsetOfExpr *E) {
     PFPFieldsWithEvaluatedOffset.insert(FD);
 }
 
+// MSVC permits certain C-style casts in constant expressions.
+// A common example is FIELD_OFFSET, implemented as
+// (LONG_PTR)(&((type*)0)->field). If MSVC compatibility is enabled
+// and the only constexpr evaluation failure is a ptr-to-int cast,
+// allow folding the expression and optionally emit a warning.
 bool ASTContext::maybeFoldConstexprWithCast(
     SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
-  if (Notes.size() != 1 || !getLangOpts().MSVCCompat)
+  if (!getLangOpts().MSVCCompat)
+    return false;
+  if (Notes.size() != 1)
     return false;
   auto &PD = Notes[0].second;
   if (PD.getDiagID() != diag::note_constexpr_invalid_cast_ptrtoint)
@@ -15644,6 +15651,5 @@ bool ASTContext::maybeFoldConstexprWithCast(
   getDiagnostics().Report(Notes[0].first, diag::warn_relaxed_constant_fold)
       << !!PD.getValueArg(0);
   Notes.clear();
-
   return true;
 }
diff --git a/clang/lib/AST/ByteCode/State.cpp b/clang/lib/AST/ByteCode/State.cpp
index 75d8fc4fefb1a..9f5c209845922 100644
--- a/clang/lib/AST/ByteCode/State.cpp
+++ b/clang/lib/AST/ByteCode/State.cpp
@@ -18,9 +18,22 @@ using namespace clang::interp;
 
 State::~State() {}
 
-// With -fms-compatibility we allow pointer to integer casts
-// followed by nullptr casts.
+// In MSVC compatibility mode we relax constexpr evaluation for the
+// FIELD_OFFSET-style pattern:
+//
+//   (LONG_PTR)(&((T*)0)->field)
+//
+// Evaluation of such expressions first produces a ptr-to-int cast
+// diagnostic and may then encounter a null-subobject access while
+// forming the field address. Both diagnostics are considered part of
+// the same accepted pattern and should not prevent constant folding.
+//
+// If a ptr-to-int cast diagnostic was recorded but evaluation later
+// reaches any other failure, discard the recorded diagnostic so the
+// expression is rejected.
 void State::clearDiagIfNeeded(diag::kind DiagId) {
+  if (!Ctx.getLangOpts().MSVCCompat)
+    return;
   switch (DiagId) {
   case diag::note_constexpr_invalid_cast_ptrtoint:
   case diag::note_constexpr_null_subobject:
@@ -28,7 +41,7 @@ void State::clearDiagIfNeeded(diag::kind DiagId) {
   }
 
   auto *Diag = EvalStatus.Diag;
-  if (!Ctx.getLangOpts().MSVCCompat || !Diag || Diag->size() != 1 ||
+  if (!Diag || Diag->size() != 1 ||
       (*Diag)[0].second.getDiagID() !=
           diag::note_constexpr_invalid_cast_ptrtoint)
     return;

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to