Skip to content

Commit 6cddfcb

Browse files
efriedma-quickrishna2803
authored andcommitted
[clang] Forbid reinterpret_cast of function pointers in constexpr. (llvm#150557)
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. Fixes llvm#150340 .
1 parent 8d30c3c commit 6cddfcb

File tree

7 files changed

+58
-15
lines changed

7 files changed

+58
-15
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -457,13 +457,17 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
457457
assert(isPtrType(*FromT));
458458
assert(isPtrType(*ToT));
459459
if (FromT == ToT) {
460-
if (CE->getType()->isVoidPointerType())
460+
if (CE->getType()->isVoidPointerType() &&
461+
!SubExprTy->isFunctionPointerType()) {
461462
return this->delegate(SubExpr);
463+
}
462464

463465
if (!this->visit(SubExpr))
464466
return false;
465-
if (CE->getType()->isFunctionPointerType())
466-
return true;
467+
if (CE->getType()->isFunctionPointerType() ||
468+
SubExprTy->isFunctionPointerType()) {
469+
return this->emitFnPtrCast(CE);
470+
}
467471
if (FromT == PT_Ptr)
468472
return this->emitPtrPtrCast(SubExprTy->isVoidPointerType(), CE);
469473
return true;

clang/lib/AST/ByteCode/Interp.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2682,6 +2682,14 @@ static inline bool CastFixedPointIntegral(InterpState &S, CodePtr OpPC) {
26822682
return true;
26832683
}
26842684

2685+
static inline bool FnPtrCast(InterpState &S, CodePtr OpPC) {
2686+
const SourceInfo &E = S.Current->getSource(OpPC);
2687+
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
2688+
<< diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
2689+
<< S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
2690+
return true;
2691+
}
2692+
26852693
static inline bool PtrPtrCast(InterpState &S, CodePtr OpPC, bool SrcIsVoidPtr) {
26862694
const auto &Ptr = S.Stk.peek<Pointer>();
26872695

clang/lib/AST/ByteCode/Opcodes.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,8 @@ def PtrPtrCast : Opcode {
735735

736736
}
737737

738+
def FnPtrCast : Opcode;
739+
738740
def DecayPtr : Opcode {
739741
let Types = [PtrTypeClass, PtrTypeClass];
740742
let HasGroup = 1;

clang/lib/AST/ExprConstant.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9741,10 +9741,19 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) {
97419741
case CK_AddressSpaceConversion:
97429742
if (!Visit(SubExpr))
97439743
return false;
9744-
// Bitcasts to cv void* are static_casts, not reinterpret_casts, so are
9745-
// permitted in constant expressions in C++11. Bitcasts from cv void* are
9746-
// also static_casts, but we disallow them as a resolution to DR1312.
9747-
if (!E->getType()->isVoidPointerType()) {
9744+
if (E->getType()->isFunctionPointerType() ||
9745+
SubExpr->getType()->isFunctionPointerType()) {
9746+
// Casting between two function pointer types, or between a function
9747+
// pointer and an object pointer, is always a reinterpret_cast.
9748+
CCEDiag(E, diag::note_constexpr_invalid_cast)
9749+
<< diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
9750+
<< Info.Ctx.getLangOpts().CPlusPlus;
9751+
Result.Designator.setInvalid();
9752+
} else if (!E->getType()->isVoidPointerType()) {
9753+
// Bitcasts to cv void* are static_casts, not reinterpret_casts, so are
9754+
// permitted in constant expressions in C++11. Bitcasts from cv void* are
9755+
// also static_casts, but we disallow them as a resolution to DR1312.
9756+
//
97489757
// In some circumstances, we permit casting from void* to cv1 T*, when the
97499758
// actual pointee object is actually a cv2 T.
97509759
bool HasValidResult = !Result.InvalidBase && !Result.Designator.Invalid &&

clang/test/AST/ByteCode/functions.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
// RUN: %clang_cc1 -pedantic -std=c++14 -verify=ref,both %s
66
// RUN: %clang_cc1 -pedantic -std=c++20 -verify=ref,both %s
77

8+
#define fold(x) (__builtin_constant_p(0) ? (x) : (x))
9+
810
constexpr void doNothing() {}
911
constexpr int gimme5() {
1012
doNothing();
@@ -654,14 +656,26 @@ namespace {
654656
}
655657

656658
namespace FunctionCast {
657-
// When folding, we allow functions to be cast to different types. Such
658-
// cast functions cannot be called, even if they're constexpr.
659+
// When folding, we allow functions to be cast to different types. We only
660+
// allow calls if the dynamic type of the pointer matches the type of the
661+
// call.
659662
constexpr int f() { return 1; }
663+
constexpr void* f2() { return nullptr; }
664+
constexpr int f3(int a) { return a; }
660665
typedef double (*DoubleFn)();
661666
typedef int (*IntFn)();
662-
int a[(int)DoubleFn(f)()]; // both-error {{variable length array}} \
663-
// both-warning {{are a Clang extension}}
664-
int b[(int)IntFn(f)()]; // ok
667+
typedef int* (*IntPtrFn)();
668+
constexpr int test1 = (int)DoubleFn(f)(); // both-error {{constant expression}} both-note {{reinterpret_cast}}
669+
// FIXME: We should print a note explaining the error.
670+
constexpr int test2 = (int)fold(DoubleFn(f))(); // both-error {{constant expression}}
671+
constexpr int test3 = (int)IntFn(f)(); // no-op cast
672+
constexpr int test4 = fold(IntFn(DoubleFn(f)))();
673+
constexpr int test5 = IntFn(fold(DoubleFn(f)))(); // both-error {{constant expression}} \
674+
// both-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}}
675+
// FIXME: Interpreter is less strict here.
676+
constexpr int test6 = fold(IntPtrFn(f2))() == nullptr; // ref-error {{constant expression}}
677+
// FIXME: The following crashes interpreter
678+
// constexpr int test6 = fold(IntFn(f3)());
665679
}
666680

667681
#if __cplusplus >= 202002L

clang/test/CXX/expr/expr.const/p2-0x.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,11 @@ namespace ReinterpretCast {
438438
struct U {
439439
int m : (long)(S*)6; // expected-warning {{constant expression}} expected-note {{reinterpret_cast}}
440440
};
441+
void f();
442+
constexpr void* fp1 = (void*)f; // expected-error {{constant expression}} expected-note {{reinterpret_cast}}
443+
constexpr int* fp2 = (int*)f; // expected-error {{constant expression}} expected-note {{reinterpret_cast}}
444+
constexpr int (*fp3)() = (int(*)())f; // expected-error {{constant expression}} expected-note {{reinterpret_cast}}
445+
constexpr int (&fp4)() = (int(&)())f; // expected-error {{constant expression}} expected-note {{reinterpret_cast}}
441446
}
442447

443448
// - a pseudo-destructor call (5.2.4);

clang/test/Sema/constexpr-void-cast.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44
// RUN: %clang_cc1 -x c -fsyntax-only %s -pedantic -verify=c-pedantic -std=c11 -fexperimental-new-constant-interpreter
55
//
66
// RUN: %clang_cc1 -x c++ -fsyntax-only %s -verify=cxx
7-
// RUN: %clang_cc1 -x c++ -fsyntax-only %s -pedantic -verify=cxx-pedantic
7+
// RUN: %clang_cc1 -x c++ -fsyntax-only %s -pedantic -verify=cxx,cxx-pedantic
88
// RUN: %clang_cc1 -x c++ -fsyntax-only %s -verify=cxx -fexperimental-new-constant-interpreter
9-
// RUN: %clang_cc1 -x c++ -fsyntax-only %s -pedantic -verify=cxx-pedantic -fexperimental-new-constant-interpreter
9+
// RUN: %clang_cc1 -x c++ -fsyntax-only %s -pedantic -verify=cxx,cxx-pedantic -fexperimental-new-constant-interpreter
1010

1111
// c-no-diagnostics
12-
// cxx-no-diagnostics
1312

1413
void f(void);
1514
struct S {char c;} s;
1615
_Static_assert(&s != (void *)&f, ""); // c-pedantic-warning {{not an integer constant expression}} \
1716
// c-pedantic-note {{this conversion is not allowed in a constant expression}} \
17+
// cxx-error {{static assertion expression is not an integral constant expression}} \
18+
// cxx-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}} \
1819
// cxx-pedantic-warning {{'_Static_assert' is a C11 extension}}

0 commit comments

Comments
 (0)