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

>From 6ebcd59c030e756acedf6a718b13a97c3c77c954 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Mon, 11 May 2026 19:39:23 +0200
Subject: [PATCH 01/18] [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 a4ed852d36442..aa20bd886a540 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3882,6 +3882,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 abf0cd5e18c2b..3ce343a2b97e3 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15663,3 +15663,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 b23bf73ae803c..b6932cc9ee767 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2590,8 +2590,12 @@ 
VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> *Notes,
   if (IsConstantInitialization &&
       (Ctx.getLangOpts().CPlusPlus ||
        (isConstexpr() && Ctx.getLangOpts().C23)) &&
-      EStatus.DiagEmitted)
-    Result = false;
+      EStatus.DiagEmitted) {
+    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 0cd95ec311218..a886339fbd72f 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18063,6 +18063,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 c663765573612..8a109531369a8 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6715,7 +6715,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 f1ef00d916d317b48e50f793652b30dea2ff5eb7 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Tue, 12 May 2026 16:52:07 +0200
Subject: [PATCH 02/18] 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 a886339fbd72f..a27732a8ca4be 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18060,11 +18060,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 8a109531369a8..a0158723768c0 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6715,7 +6715,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 e19211ed93792f2ce752d1f2355674566be18317 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Tue, 12 May 2026 19:22:43 +0200
Subject: [PATCH 03/18] 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 aa20bd886a540..710c8d592a6ff 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3882,8 +3882,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 1c9d021317289..ba2ce6e46a8d7 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1673,6 +1673,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 3ce343a2b97e3..50bae3960289f 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15664,8 +15664,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 b6932cc9ee767..271e099874eef 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2591,10 +2591,8 @@ 
VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> *Notes,
       (Ctx.getLangOpts().CPlusPlus ||
        (isConstexpr() && Ctx.getLangOpts().C23)) &&
       EStatus.DiagEmitted) {
-    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 a27732a8ca4be..007fdf7e59d2e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18062,9 +18062,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 a0158723768c0..6480a9923e1eb 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6717,9 +6717,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 a47abeac7ed1a03435d3fda9cd6eb721ffcca457 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Thu, 14 May 2026 17:00:59 +0200
Subject: [PATCH 04/18] 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 63b619b4dbd0154bd2e8e3186a233cce62805aa9 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Thu, 14 May 2026 17:07:02 +0200
Subject: [PATCH 05/18] 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 50bae3960289f..adec8dbe91d8a 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15666,8 +15666,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 96e6edf9f778b778746cc29c7646924e02b686e5 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Thu, 14 May 2026 20:02:28 +0200
Subject: [PATCH 06/18] 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 adec8dbe91d8a..0735a9c9b6a72 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15666,14 +15666,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 e101b3a18258dc06c69147f09c0690195040ada6 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Mon, 18 May 2026 13:50:18 +0200
Subject: [PATCH 07/18] 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 0735a9c9b6a72..0f971e656cb87 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15669,23 +15669,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 563d6b3bb0cf9..28067162d8640 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -8539,7 +8539,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);
   }
@@ -19655,7 +19655,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 3ad7ac3917e42207613ef1c4549a5a7ffefbefb0 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Tue, 19 May 2026 14:49:52 +0200
Subject: [PATCH 08/18] 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 710c8d592a6ff..e0a1fd498e074 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3882,7 +3882,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 0f971e656cb87..7ad326917ed0c 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15665,8 +15665,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 271e099874eef..4da23daf04354 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2591,7 +2591,7 @@ 
VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> *Notes,
       (Ctx.getLangOpts().CPlusPlus ||
        (isConstexpr() && Ctx.getLangOpts().C23)) &&
       EStatus.DiagEmitted) {
-    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 007fdf7e59d2e..39b838e844cc4 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18063,7 +18063,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 6480a9923e1eb..e94873782cc62 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6718,7 +6718,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 5562fe24c16ccf140dfc6c7a627ba14437c03c81 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Mon, 25 May 2026 11:46:54 +0200
Subject: [PATCH 09/18] 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 e0a1fd498e074..369b2c14e7f51 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3882,8 +3882,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 7ad326917ed0c..5f20b42c4d336 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15664,7 +15664,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 4da23daf04354..b70672df78fb7 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2591,7 +2591,7 @@ 
VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> *Notes,
       (Ctx.getLangOpts().CPlusPlus ||
        (isConstexpr() && Ctx.getLangOpts().C23)) &&
       EStatus.DiagEmitted) {
-    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 39b838e844cc4..826c9691255ce 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18063,7 +18063,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 e94873782cc62..8866b6eae41e9 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6718,7 +6718,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 28757344bca605b3178dc4074a1f3d6f08ee63c7 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Mon, 25 May 2026 16:03:26 +0200
Subject: [PATCH 10/18] 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 b70672df78fb7..1e6407c4253a7 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2587,7 +2587,7 @@ 
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)) &&
       EStatus.DiagEmitted) {
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 28067162d8640..2b646b54d0ffd 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -8539,7 +8539,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 610039925349121d1a718b35fbee00540e05c38c Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Tue, 26 May 2026 15:20:17 +0200
Subject: [PATCH 11/18] 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              | 1 +
 clang/lib/Sema/SemaExpr.cpp                 | 4 ++--
 clang/lib/Sema/SemaOverload.cpp             | 4 ++--
 clang/test/SemaCXX/microsoft-constexpr2.cpp | 2 ++
 8 files changed, 17 insertions(+), 11 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h 
b/clang/include/clang/AST/ASTContext.h
index 369b2c14e7f51..6dea527839e9a 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3882,8 +3882,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 b016126e9a813..9c1c4bfaf09ea 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -622,6 +622,10 @@ class Expr : public ValueStmt {
     /// Whether any diagnostic has been emitted. This is set regardless of
     /// whether @ref #Diag is set or not.
     bool DiagEmitted = 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
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 5f20b42c4d336..68a5a79445073 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15665,8 +15665,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 1e6407c4253a7..4d36bd29fe1a8 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2587,12 +2587,11 @@ 
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)) &&
       EStatus.DiagEmitted) {
-    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 2b646b54d0ffd..82904547164c8 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -19666,6 +19666,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.
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 826c9691255ce..0cd92e068d5d0 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18062,8 +18062,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 8866b6eae41e9..5ce05b6f11dc5 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6717,8 +6717,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 2994f27c6d54161383df615d35d8ab9d3a923a0d Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Tue, 26 May 2026 21:35:51 +0200
Subject: [PATCH 12/18] 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 8c8d3b3a25a64995fb8dc6870b154c7471e0a7a9 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Mon, 1 Jun 2026 14:41:39 +0200
Subject: [PATCH 13/18] 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 e62b77272046c..84ec99b99308f 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);
@@ -44,6 +61,7 @@ OptionalDiagnostic State::FFDiag(SourceInfo SI, diag::kind 
DiagId,
 OptionalDiagnostic State::CCEDiag(SourceLocation Loc, diag::kind DiagId,
                                   unsigned ExtraNotes) {
   EvalStatus.DiagEmitted = true;
+  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 df3afdf8cbc24..4bfff6b939f6a 100644
--- a/clang/lib/AST/ByteCode/State.h
+++ b/clang/lib/AST/ByteCode/State.h
@@ -92,6 +92,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 b618f583353cd0fc6b7915d82f52086df64dcb13 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Wed, 3 Jun 2026 10:00:08 +0200
Subject: [PATCH 14/18] 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 68a5a79445073..2af073a93cc0f 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15664,9 +15664,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)
@@ -15674,6 +15681,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 84ec99b99308f..9c2f7956992ba 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;

>From 995b23494fdb1e2a82d1609e09c9816d0642c0dd Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Fri, 5 Jun 2026 17:34:41 +0200
Subject: [PATCH 15/18] Change how we evaluate expressions under
 -fms-compatibility

1. Ignore invalid_cast_ptrtoint and null_subobject notes in MS
   compatibility mode, because they're not considered an error.

2. LValue casts are treated as an error, so separate note is emitted for
   this. After seeing it frontend naturally emits an error.

3. In SFINAE context we simply report evaluation error, so emitting note
   is not needed.
---
 clang/include/clang/AST/ASTContext.h          |  3 --
 clang/include/clang/AST/Expr.h                |  8 +++++
 .../include/clang/Basic/DiagnosticASTKinds.td |  5 +--
 clang/lib/AST/ASTContext.cpp                  | 20 ------------
 clang/lib/AST/ByteCode/State.cpp              | 32 ++++---------------
 clang/lib/AST/ByteCode/State.h                |  2 +-
 clang/lib/AST/ExprConstant.cpp                | 14 ++++++--
 clang/lib/Sema/SemaExpr.cpp                   |  4 +--
 clang/lib/Sema/SemaOverload.cpp               |  6 ++--
 clang/test/SemaCXX/microsoft-constexpr.cpp    |  1 -
 clang/test/SemaCXX/microsoft-constexpr2.cpp   | 18 ++++++++---
 11 files changed, 49 insertions(+), 64 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h 
b/clang/include/clang/AST/ASTContext.h
index 6dea527839e9a..a4ed852d36442 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3882,9 +3882,6 @@ OPT_LIST(V)
   void recordMemberDataPointerEvaluation(const ValueDecl *VD);
   void recordOffsetOfEvaluation(const OffsetOfExpr *E);
 
-  bool
-  maybeFoldConstexprWithCast(SmallVectorImpl<PartialDiagnosticAt> &Notes) 
const;
-
 private:
   /// All OMPTraitInfo objects live in this collection, one per
   /// `pragma omp [begin] declare variant` directive.
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 9c1c4bfaf09ea..102dcbc7d7be6 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -626,6 +626,14 @@ class Expr : public ValueStmt {
     /// Whether part of expression is an LValue.
     /// Used when evaluating constant expression with Microsoft extensions.
     bool HasLValue = false;
+    
+    /// Whether we've seen a ptr to int cast or null subobject while evaluating
+    /// constant expression in MS compatibility mode.
+    bool SeenCastOrNull = false;
+
+    /// Whether the expression being evaluated is converted from some other
+    /// expression. This is used to suppress duplicate warnings
+    bool IsConvertedExpr = 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
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td 
b/clang/include/clang/Basic/DiagnosticASTKinds.td
index ce864bdb1fe8a..adec7cf07f58f 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -20,6 +20,7 @@ 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_has_lvalue : Note<"constant expression contains l-value">;
 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 "
@@ -1036,8 +1037,8 @@ def warn_npot_ms_struct : Warning<
   DefaultError, InGroup<IncompatibleMSStruct>;
 def warn_relaxed_constant_fold : Extension<
   "folding constant expression involving "
-  "%select{reinterpret_cast|cast that performs the conversions of a 
reinterpret_cast}0"
-  " is a Microsoft extension">,
+  "cast that performs the conversions of a reinterpret_cast "
+  "is a Microsoft extension">,
   InGroup<MicrosoftRelaxedConstantFold>;
 
 def err_itanium_layout_unimplemented : Error<
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 2af073a93cc0f..abf0cd5e18c2b 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15663,23 +15663,3 @@ void ASTContext::recordOffsetOfEvaluation(const 
OffsetOfExpr *E) {
   if (FieldDecl *FD = Comp.getField(); isPFPField(FD))
     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 (!getLangOpts().MSVCCompat)
-    return false;
-  if (Notes.size() != 1)
-    return false;
-  auto &PD = Notes[0].second;
-  if (PD.getDiagID() != diag::note_constexpr_invalid_cast_ptrtoint)
-    return false;
-  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 9c2f7956992ba..d038c730c8d38 100644
--- a/clang/lib/AST/ByteCode/State.cpp
+++ b/clang/lib/AST/ByteCode/State.cpp
@@ -18,34 +18,17 @@ using namespace clang::interp;
 
 State::~State() {}
 
-// 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) {
+bool State::shouldRelaxDiag(diag::kind DiagId) {
   if (!Ctx.getLangOpts().MSVCCompat)
-    return;
+    return false;
   switch (DiagId) {
   case diag::note_constexpr_invalid_cast_ptrtoint:
   case diag::note_constexpr_null_subobject:
-    return;
+    EvalStatus.SeenCastOrNull = true;
+    return true;
+  default:
+    return false;
   }
-
-  auto *Diag = EvalStatus.Diag;
-  if (!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,
@@ -74,7 +57,6 @@ OptionalDiagnostic State::FFDiag(SourceInfo SI, diag::kind 
DiagId,
 OptionalDiagnostic State::CCEDiag(SourceLocation Loc, diag::kind DiagId,
                                   unsigned ExtraNotes) {
   EvalStatus.DiagEmitted = true;
-  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()) {
@@ -118,7 +100,7 @@ PartialDiagnostic &State::addDiag(SourceLocation Loc, 
diag::kind DiagId) {
 
 OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId,
                                unsigned ExtraNotes, bool IsCCEDiag) {
-  if (EvalStatus.Diag) {
+  if (EvalStatus.Diag && !shouldRelaxDiag(DiagId)) {
     if (hasPriorDiagnostic()) {
       return OptionalDiagnostic();
     }
diff --git a/clang/lib/AST/ByteCode/State.h b/clang/lib/AST/ByteCode/State.h
index 4bfff6b939f6a..5efef3f707042 100644
--- a/clang/lib/AST/ByteCode/State.h
+++ b/clang/lib/AST/ByteCode/State.h
@@ -92,7 +92,7 @@ class State {
   ASTContext &getASTContext() const { return Ctx; }
   const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); }
 
-  void clearDiagIfNeeded(diag::kind DiagId);
+  bool shouldRelaxDiag(diag::kind DiagId);
 
   /// Note that we have had a side-effect, and determine whether we should
   /// keep evaluating.
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 82904547164c8..c67769c6e656d 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2423,6 +2423,14 @@ static bool CheckLiteralType(EvalInfo &Info, const Expr 
*E,
   return false;
 }
 
+static void CheckMicrosoftRelaxations(EvalInfo &Info,
+                                      const SourceLocation &Loc) {
+  auto *Diag = Info.EvalStatus.Diag;
+  if (Diag && Diag->empty() && Info.EvalStatus.SeenCastOrNull &&
+      !Info.EvalStatus.IsConvertedExpr)
+    Info.report(Loc, diag::warn_relaxed_constant_fold);
+}
+
 static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
                                   EvalInfo &Info, SourceLocation DiagLoc,
                                   QualType Type, const APValue &Value,
@@ -2511,6 +2519,9 @@ static bool 
CheckEvaluationResult(CheckEvaluationResultKind CERK,
       CERK == CheckEvaluationResultKind::ConstantExpression)
     return CheckMemberPointerConstantExpression(Info, DiagLoc, Type, Value, 
Kind);
 
+  // Emit warning if expression is not LValue, member pointer,
+  // and contains C-style casts under -fms-compatibility
+  CheckMicrosoftRelaxations(Info, DiagLoc);
   // Everything else is fine.
   return true;
 }
@@ -19666,7 +19677,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) 
{
       return false;
 
     if (LV.getLValueBase()) {
-      Info.EvalStatus.HasLValue = true;
+      CCEDiag(E, diag::note_constexpr_has_lvalue) << E->getSourceRange();
       // 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.
@@ -21646,7 +21657,6 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, 
const ASTContext &Ctx,
     // destruction.
     return false;
   }
-
   return true;
 }
 
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 0cd92e068d5d0..d63cecb76dea6 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18062,8 +18062,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() && !EvalResult.HasLValue)
-    getASTContext().maybeFoldConstexprWithCast(Notes);
+  if (isSFINAEContext() && EvalResult.SeenCastOrNull)
+    Folded = false;
 
   // 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 5ce05b6f11dc5..7a4875076691a 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6697,6 +6697,7 @@ Sema::EvaluateConvertedConstantExpression(Expr *E, 
QualType T, APValue &Value,
   SmallVector<PartialDiagnosticAt, 8> Notes;
   Expr::EvalResult Eval;
   Eval.Diag = &Notes;
+  Eval.IsConvertedExpr = true;
 
   assert(CCE != CCEKind::TempArgStrict && "unnexpected CCE Kind");
 
@@ -6717,9 +6718,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() && !Eval.HasLValue)
-      getASTContext().maybeFoldConstexprWithCast(Notes);
-    if (Notes.empty()) {
+    bool CantFold = isSFINAEContext() && Eval.SeenCastOrNull;
+    if (Notes.empty() && !CantFold) {
       // It's a constant expression.
       Expr *E = Result.get();
       if (const auto *CE = dyn_cast<ConstantExpr>(E)) {
diff --git a/clang/test/SemaCXX/microsoft-constexpr.cpp 
b/clang/test/SemaCXX/microsoft-constexpr.cpp
index fb0a849e5ce7b..a473e51a55f4c 100644
--- a/clang/test/SemaCXX/microsoft-constexpr.cpp
+++ b/clang/test/SemaCXX/microsoft-constexpr.cpp
@@ -50,7 +50,6 @@ 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) {}
 
diff --git a/clang/test/SemaCXX/microsoft-constexpr2.cpp 
b/clang/test/SemaCXX/microsoft-constexpr2.cpp
index 1b917e635d66f..8a1aee4dc8bcb 100644
--- a/clang/test/SemaCXX/microsoft-constexpr2.cpp
+++ b/clang/test/SemaCXX/microsoft-constexpr2.cpp
@@ -10,17 +10,25 @@ struct S {
   int y;
 } ob;
 
+
+template<bool B>
+struct TplBool {};
+
+TplBool<FIELD_OFFSET(S, y)> tc; // expected-error {{non-type template argument 
evaluates to 4, which cannot be narrowed to type 'bool'}}
+                               // expected-warning@-1 {{folding constant 
expression involving cast that performs the conversions of a reinterpret_cast 
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}}
+constexpr long b2 = FIELD_OFFSET2(S, y); // expected-warning {{folding 
constant expression involving cast that performs the conversions of a 
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}}
+                                      // expected-note@-1 {{constant 
expression contains l-value}}
 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}}
+                (8 + reinterpret_cast<LONG_PTR>(&ob));    // expected-note 
{{constant expression contains l-value}}
 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}}
+                                                         // expected-note@-1 
{{constant expression contains l-value}}
 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}}
+                                                                 // 
expected-note@-1 {{constant expression contains l-value}}
 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}}
+constexpr LONG_PTR b9  = (LONG_PTR)(FIELD_OFFSET(S, y) / 0); // expected-error 
{{constexpr variable 'b9' must be initialized by a constant expression}}
+                                                            // 
expected-note@-1 {{division by zero}}

>From 81b54ddc49d98401ce732e88d281e6ca8e8c5d89 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Fri, 5 Jun 2026 17:58:10 +0200
Subject: [PATCH 16/18] Remove unused code

---
 clang/include/clang/Basic/PartialDiagnostic.h | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/clang/include/clang/Basic/PartialDiagnostic.h 
b/clang/include/clang/Basic/PartialDiagnostic.h
index 7469e45f7d888..4bf6049d08fdb 100644
--- a/clang/include/clang/Basic/PartialDiagnostic.h
+++ b/clang/include/clang/Basic/PartialDiagnostic.h
@@ -189,14 +189,6 @@ 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 a value arg");
-    return DiagStorage->DiagArgumentsVal[I];
-  }
 };
 
 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,

>From e4813fcfc250ebb0ed435c30fcbb8b79ade4288e Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Fri, 5 Jun 2026 18:13:16 +0200
Subject: [PATCH 17/18] Remove unused code #2

---
 clang/lib/AST/Decl.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 4d36bd29fe1a8..b23bf73ae803c 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2590,9 +2590,8 @@ 
VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> *Notes,
   if (IsConstantInitialization &&
       (Ctx.getLangOpts().CPlusPlus ||
        (isConstexpr() && Ctx.getLangOpts().C23)) &&
-      EStatus.DiagEmitted) {
+      EStatus.DiagEmitted)
     Result = false;
-  }
 
   // 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

>From 22c8b970e47f03f480f7b684df7b5aad60e0a5f7 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Tue, 30 Jun 2026 16:37:09 +0200
Subject: [PATCH 18/18] Rebase and fix issues

---
 clang/include/clang/AST/Expr.h              | 4 ++--
 clang/lib/AST/ByteCode/State.cpp            | 6 +++++-
 clang/test/SemaCXX/microsoft-constexpr2.cpp | 1 -
 3 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 102dcbc7d7be6..44a0c1e0e997a 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -622,11 +622,11 @@ class Expr : public ValueStmt {
     /// Whether any diagnostic has been emitted. This is set regardless of
     /// whether @ref #Diag is set or not.
     bool DiagEmitted = false;
-    
+
     /// Whether part of expression is an LValue.
     /// Used when evaluating constant expression with Microsoft extensions.
     bool HasLValue = false;
-    
+
     /// Whether we've seen a ptr to int cast or null subobject while evaluating
     /// constant expression in MS compatibility mode.
     bool SeenCastOrNull = false;
diff --git a/clang/lib/AST/ByteCode/State.cpp b/clang/lib/AST/ByteCode/State.cpp
index d038c730c8d38..21e2bb6018af4 100644
--- a/clang/lib/AST/ByteCode/State.cpp
+++ b/clang/lib/AST/ByteCode/State.cpp
@@ -56,6 +56,10 @@ OptionalDiagnostic State::FFDiag(SourceInfo SI, diag::kind 
DiagId,
 
 OptionalDiagnostic State::CCEDiag(SourceLocation Loc, diag::kind DiagId,
                                   unsigned ExtraNotes) {
+  if (shouldRelaxDiag(DiagId)) {
+    setActiveDiagnostic(false);
+    return OptionalDiagnostic();
+  }
   EvalStatus.DiagEmitted = true;
   // Don't override a previous diagnostic. Don't bother collecting
   // diagnostics if we're evaluating for overflow.
@@ -100,7 +104,7 @@ PartialDiagnostic &State::addDiag(SourceLocation Loc, 
diag::kind DiagId) {
 
 OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId,
                                unsigned ExtraNotes, bool IsCCEDiag) {
-  if (EvalStatus.Diag && !shouldRelaxDiag(DiagId)) {
+  if (EvalStatus.Diag) {
     if (hasPriorDiagnostic()) {
       return OptionalDiagnostic();
     }
diff --git a/clang/test/SemaCXX/microsoft-constexpr2.cpp 
b/clang/test/SemaCXX/microsoft-constexpr2.cpp
index 8a1aee4dc8bcb..6fd70135361f7 100644
--- a/clang/test/SemaCXX/microsoft-constexpr2.cpp
+++ b/clang/test/SemaCXX/microsoft-constexpr2.cpp
@@ -15,7 +15,6 @@ template<bool B>
 struct TplBool {};
 
 TplBool<FIELD_OFFSET(S, y)> tc; // expected-error {{non-type template argument 
evaluates to 4, which cannot be narrowed to type 'bool'}}
-                               // expected-warning@-1 {{folding constant 
expression involving cast that performs the conversions of a reinterpret_cast 
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 cast that performs the conversions of a 
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}}

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

Reply via email to