https://github.com/efriedma-quic updated 
https://github.com/llvm/llvm-project/pull/150557

>From f111a98680ba2301ee93a45a3c30630c71925562 Mon Sep 17 00:00:00 2001
From: Eli Friedman <efrie...@quicinc.com>
Date: Thu, 24 Jul 2025 17:22:27 -0700
Subject: [PATCH 1/2] [clang] Forbid reinterpret_cast of function pointers in
 constexpr.

This has been explicitly forbidden since C++11, but somehow the edge
case of converting a function pointer to void* using a cast like
`(void*)f` wasn't handled.
---
 clang/lib/AST/ExprConstant.cpp           | 17 +++++++++++++----
 clang/test/CXX/expr/expr.const/p2-0x.cpp |  5 +++++
 clang/test/Sema/constexpr-void-cast.c    |  6 ++++--
 3 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 9808298a1b1d0..993b64b2752e9 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -9741,10 +9741,19 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr 
*E) {
   case CK_AddressSpaceConversion:
     if (!Visit(SubExpr))
       return false;
-    // Bitcasts to cv void* are static_casts, not reinterpret_casts, so are
-    // permitted in constant expressions in C++11. Bitcasts from cv void* are
-    // also static_casts, but we disallow them as a resolution to DR1312.
-    if (!E->getType()->isVoidPointerType()) {
+    if (E->getType()->isFunctionPointerType() ||
+        SubExpr->getType()->isFunctionPointerType()) {
+      // Casting between two function pointer types, or between a function
+      // pointer and an object pointer, is always a reinterpret_cast.
+      CCEDiag(E, diag::note_constexpr_invalid_cast)
+          << diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
+          << Info.Ctx.getLangOpts().CPlusPlus;
+      Result.Designator.setInvalid();
+    } else if (!E->getType()->isVoidPointerType()) {
+      // Bitcasts to cv void* are static_casts, not reinterpret_casts, so are
+      // permitted in constant expressions in C++11. Bitcasts from cv void* are
+      // also static_casts, but we disallow them as a resolution to DR1312.
+      //
       // In some circumstances, we permit casting from void* to cv1 T*, when 
the
       // actual pointee object is actually a cv2 T.
       bool HasValidResult = !Result.InvalidBase && !Result.Designator.Invalid 
&&
diff --git a/clang/test/CXX/expr/expr.const/p2-0x.cpp 
b/clang/test/CXX/expr/expr.const/p2-0x.cpp
index 910c8635f7353..8401d3033eda9 100644
--- a/clang/test/CXX/expr/expr.const/p2-0x.cpp
+++ b/clang/test/CXX/expr/expr.const/p2-0x.cpp
@@ -438,6 +438,11 @@ namespace ReinterpretCast {
   struct U {
     int m : (long)(S*)6; // expected-warning {{constant expression}} 
expected-note {{reinterpret_cast}}
   };
+  void f();
+  constexpr void* fp1 = (void*)f; // expected-error {{constant expression}} 
expected-note {{reinterpret_cast}}
+  constexpr int* fp2 = (int*)f; // expected-error {{constant expression}} 
expected-note {{reinterpret_cast}}
+  constexpr int (*fp3)() = (int(*)())f; // expected-error {{constant 
expression}} expected-note {{reinterpret_cast}}
+  constexpr int (&fp4)() = (int(&)())f; // expected-error {{constant 
expression}} expected-note {{reinterpret_cast}}
 }
 
 // - a pseudo-destructor call (5.2.4);
diff --git a/clang/test/Sema/constexpr-void-cast.c 
b/clang/test/Sema/constexpr-void-cast.c
index 2ffc59f509b4b..ffaed9a263ace 100644
--- a/clang/test/Sema/constexpr-void-cast.c
+++ b/clang/test/Sema/constexpr-void-cast.c
@@ -3,8 +3,8 @@
 // RUN: %clang_cc1 -x c -fsyntax-only %s -verify=c -std=c11 
-fexperimental-new-constant-interpreter
 // RUN: %clang_cc1 -x c -fsyntax-only %s -pedantic -verify=c-pedantic -std=c11 
-fexperimental-new-constant-interpreter
 //
-// RUN: %clang_cc1 -x c++ -fsyntax-only %s -verify=cxx
-// RUN: %clang_cc1 -x c++ -fsyntax-only %s -pedantic -verify=cxx-pedantic
+// RUN: %clang_cc1 -x c++ -fsyntax-only %s -verify=cxx-nointerpreter
+// RUN: %clang_cc1 -x c++ -fsyntax-only %s -pedantic 
-verify=cxx-pedantic,cxx-nointerpreter
 // RUN: %clang_cc1 -x c++ -fsyntax-only %s -verify=cxx 
-fexperimental-new-constant-interpreter
 // RUN: %clang_cc1 -x c++ -fsyntax-only %s -pedantic -verify=cxx-pedantic 
-fexperimental-new-constant-interpreter
 
@@ -15,4 +15,6 @@ void f(void);
 struct S {char c;} s;
 _Static_assert(&s != (void *)&f, ""); // c-pedantic-warning {{not an integer 
constant expression}} \
                                       // c-pedantic-note {{this conversion is 
not allowed in a constant expression}} \
+                                      // cxx-nointerpreter-error {{static 
assertion expression is not an integral constant expression}} \
+                                      // cxx-nointerpreter-note {{cast that 
performs the conversions of a reinterpret_cast is not allowed in a constant 
expression}} \
                                       // cxx-pedantic-warning 
{{'_Static_assert' is a C11 extension}}

>From 48f84b3ecbc356f4c8c224aafb0ef8597be9ab9d Mon Sep 17 00:00:00 2001
From: Eli Friedman <efrie...@quicinc.com>
Date: Fri, 25 Jul 2025 12:35:51 -0700
Subject: [PATCH 2/2] Add interpreter support.

---
 clang/lib/AST/ByteCode/Compiler.cpp   | 10 +++++++---
 clang/lib/AST/ByteCode/Interp.h       |  8 ++++++++
 clang/lib/AST/ByteCode/Opcodes.td     |  2 ++
 clang/test/Sema/constexpr-void-cast.c | 11 +++++------
 4 files changed, 22 insertions(+), 9 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 63ac536c2b445..69d75bcc7634a 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -457,13 +457,17 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) 
{
     assert(isPtrType(*FromT));
     assert(isPtrType(*ToT));
     if (FromT == ToT) {
-      if (CE->getType()->isVoidPointerType())
+      if (CE->getType()->isVoidPointerType() &&
+          !SubExprTy->isFunctionPointerType()) {
         return this->delegate(SubExpr);
+      }
 
       if (!this->visit(SubExpr))
         return false;
-      if (CE->getType()->isFunctionPointerType())
-        return true;
+      if (CE->getType()->isFunctionPointerType() ||
+          SubExprTy->isFunctionPointerType()) {
+        return this->emitFnPtrCast(CE);
+      }
       if (FromT == PT_Ptr)
         return this->emitPtrPtrCast(SubExprTy->isVoidPointerType(), CE);
       return true;
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 9012442943d59..5087425af0e33 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -2688,6 +2688,14 @@ static inline bool CastFixedPointIntegral(InterpState 
&S, CodePtr OpPC) {
   return true;
 }
 
+static inline bool FnPtrCast(InterpState &S, CodePtr OpPC) {
+  const SourceInfo &E = S.Current->getSource(OpPC);
+  S.CCEDiag(E, diag::note_constexpr_invalid_cast)
+      << diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
+      << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
+  return true;
+}
+
 static inline bool PtrPtrCast(InterpState &S, CodePtr OpPC, bool SrcIsVoidPtr) 
{
   const auto &Ptr = S.Stk.peek<Pointer>();
 
diff --git a/clang/lib/AST/ByteCode/Opcodes.td 
b/clang/lib/AST/ByteCode/Opcodes.td
index abfed77750f87..54abf22e59393 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -735,6 +735,8 @@ def PtrPtrCast : Opcode {
 
 }
 
+def FnPtrCast : Opcode;
+
 def DecayPtr : Opcode {
   let Types = [PtrTypeClass, PtrTypeClass];
   let HasGroup = 1;
diff --git a/clang/test/Sema/constexpr-void-cast.c 
b/clang/test/Sema/constexpr-void-cast.c
index ffaed9a263ace..cac671e292f56 100644
--- a/clang/test/Sema/constexpr-void-cast.c
+++ b/clang/test/Sema/constexpr-void-cast.c
@@ -3,18 +3,17 @@
 // RUN: %clang_cc1 -x c -fsyntax-only %s -verify=c -std=c11 
-fexperimental-new-constant-interpreter
 // RUN: %clang_cc1 -x c -fsyntax-only %s -pedantic -verify=c-pedantic -std=c11 
-fexperimental-new-constant-interpreter
 //
-// RUN: %clang_cc1 -x c++ -fsyntax-only %s -verify=cxx-nointerpreter
-// RUN: %clang_cc1 -x c++ -fsyntax-only %s -pedantic 
-verify=cxx-pedantic,cxx-nointerpreter
+// RUN: %clang_cc1 -x c++ -fsyntax-only %s -verify=cxx
+// RUN: %clang_cc1 -x c++ -fsyntax-only %s -pedantic -verify=cxx,cxx-pedantic
 // RUN: %clang_cc1 -x c++ -fsyntax-only %s -verify=cxx 
-fexperimental-new-constant-interpreter
-// RUN: %clang_cc1 -x c++ -fsyntax-only %s -pedantic -verify=cxx-pedantic 
-fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -x c++ -fsyntax-only %s -pedantic -verify=cxx,cxx-pedantic 
-fexperimental-new-constant-interpreter
 
 // c-no-diagnostics
-// cxx-no-diagnostics
 
 void f(void);
 struct S {char c;} s;
 _Static_assert(&s != (void *)&f, ""); // c-pedantic-warning {{not an integer 
constant expression}} \
                                       // c-pedantic-note {{this conversion is 
not allowed in a constant expression}} \
-                                      // cxx-nointerpreter-error {{static 
assertion expression is not an integral constant expression}} \
-                                      // cxx-nointerpreter-note {{cast that 
performs the conversions of a reinterpret_cast is not allowed in a constant 
expression}} \
+                                      // cxx-error {{static assertion 
expression is not an integral constant expression}} \
+                                      // cxx-note {{cast that performs the 
conversions of a reinterpret_cast is not allowed in a constant expression}} \
                                       // cxx-pedantic-warning 
{{'_Static_assert' is a C11 extension}}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to