[clang] Forbid reinterpret_cast of function pointers in constexpr. (#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 #150340 .
This commit is contained in:
parent
d3f500f2d9
commit
0bbe1b30fa
@ -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;
|
||||
|
@ -2682,6 +2682,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>();
|
||||
|
||||
|
@ -735,6 +735,8 @@ def PtrPtrCast : Opcode {
|
||||
|
||||
}
|
||||
|
||||
def FnPtrCast : Opcode;
|
||||
|
||||
def DecayPtr : Opcode {
|
||||
let Types = [PtrTypeClass, PtrTypeClass];
|
||||
let HasGroup = 1;
|
||||
|
@ -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 &&
|
||||
|
@ -5,6 +5,8 @@
|
||||
// RUN: %clang_cc1 -pedantic -std=c++14 -verify=ref,both %s
|
||||
// RUN: %clang_cc1 -pedantic -std=c++20 -verify=ref,both %s
|
||||
|
||||
#define fold(x) (__builtin_constant_p(0) ? (x) : (x))
|
||||
|
||||
constexpr void doNothing() {}
|
||||
constexpr int gimme5() {
|
||||
doNothing();
|
||||
@ -654,14 +656,26 @@ namespace {
|
||||
}
|
||||
|
||||
namespace FunctionCast {
|
||||
// When folding, we allow functions to be cast to different types. Such
|
||||
// cast functions cannot be called, even if they're constexpr.
|
||||
// When folding, we allow functions to be cast to different types. We only
|
||||
// allow calls if the dynamic type of the pointer matches the type of the
|
||||
// call.
|
||||
constexpr int f() { return 1; }
|
||||
constexpr void* f2() { return nullptr; }
|
||||
constexpr int f3(int a) { return a; }
|
||||
typedef double (*DoubleFn)();
|
||||
typedef int (*IntFn)();
|
||||
int a[(int)DoubleFn(f)()]; // both-error {{variable length array}} \
|
||||
// both-warning {{are a Clang extension}}
|
||||
int b[(int)IntFn(f)()]; // ok
|
||||
typedef int* (*IntPtrFn)();
|
||||
constexpr int test1 = (int)DoubleFn(f)(); // both-error {{constant expression}} both-note {{reinterpret_cast}}
|
||||
// FIXME: We should print a note explaining the error.
|
||||
constexpr int test2 = (int)fold(DoubleFn(f))(); // both-error {{constant expression}}
|
||||
constexpr int test3 = (int)IntFn(f)(); // no-op cast
|
||||
constexpr int test4 = fold(IntFn(DoubleFn(f)))();
|
||||
constexpr int test5 = IntFn(fold(DoubleFn(f)))(); // both-error {{constant expression}} \
|
||||
// both-note {{cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression}}
|
||||
// FIXME: Interpreter is less strict here.
|
||||
constexpr int test6 = fold(IntPtrFn(f2))() == nullptr; // ref-error {{constant expression}}
|
||||
// FIXME: The following crashes interpreter
|
||||
// constexpr int test6 = fold(IntFn(f3)());
|
||||
}
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
|
@ -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);
|
||||
|
@ -4,15 +4,16 @@
|
||||
// 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 -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-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}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user