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