Skip to content

Commit 1a78ef9

Browse files
authored
[clang][bytecode] Allow casts from void* only in std::allocator calls (#136714)
Otherwise, add the missing diagnostic.
1 parent 5080a02 commit 1a78ef9

File tree

8 files changed

+79
-65
lines changed

8 files changed

+79
-65
lines changed

clang/lib/AST/ByteCode/Interp.h

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,12 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
11131113
<< P.toDiagnosticString(S.getASTContext());
11141114
return false;
11151115
}
1116+
} else if (BothNonNull && P.isIntegralPointer()) {
1117+
const SourceInfo &Loc = S.Current->getSource(OpPC);
1118+
S.FFDiag(Loc, diag::note_constexpr_pointer_constant_comparison)
1119+
<< LHS.toDiagnosticString(S.getASTContext())
1120+
<< RHS.toDiagnosticString(S.getASTContext());
1121+
return false;
11161122
}
11171123
}
11181124

@@ -2389,7 +2395,18 @@ static inline bool PtrPtrCast(InterpState &S, CodePtr OpPC, bool SrcIsVoidPtr) {
23892395
bool HasValidResult = !Ptr.isZero();
23902396

23912397
if (HasValidResult) {
2392-
// FIXME: note_constexpr_invalid_void_star_cast
2398+
if (S.getStdAllocatorCaller("allocate"))
2399+
return true;
2400+
2401+
const auto &E = cast<CastExpr>(S.Current->getExpr(OpPC));
2402+
if (S.getLangOpts().CPlusPlus26 &&
2403+
S.getASTContext().hasSimilarType(Ptr.getType(),
2404+
E->getType()->getPointeeType()))
2405+
return true;
2406+
2407+
S.CCEDiag(E, diag::note_constexpr_invalid_void_star_cast)
2408+
<< E->getSubExpr()->getType() << S.getLangOpts().CPlusPlus26
2409+
<< Ptr.getType().getCanonicalType() << E->getType()->getPointeeType();
23932410
} else if (!S.getLangOpts().CPlusPlus26) {
23942411
const SourceInfo &E = S.Current->getSource(OpPC);
23952412
S.CCEDiag(E, diag::note_constexpr_invalid_cast)
@@ -2781,10 +2798,9 @@ template <PrimType Name, class T = typename PrimConv<Name>::T>
27812798
inline bool GetIntPtr(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
27822799
const T &IntVal = S.Stk.pop<T>();
27832800

2784-
if (Desc)
2785-
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_invalid_cast)
2786-
<< diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
2787-
<< S.getLangOpts().CPlusPlus;
2801+
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_invalid_cast)
2802+
<< diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
2803+
<< S.getLangOpts().CPlusPlus;
27882804

27892805
S.Stk.push<Pointer>(static_cast<uint64_t>(IntVal), Desc);
27902806
return true;

clang/lib/AST/ByteCode/InterpBuiltin.cpp

Lines changed: 2 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,34 +1526,7 @@ static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC,
15261526
// A call to __operator_new is only valid within std::allocate<>::allocate.
15271527
// Walk up the call stack to find the appropriate caller and get the
15281528
// element type from it.
1529-
QualType ElemType;
1530-
const CallExpr *NewCall = nullptr;
1531-
1532-
for (const InterpFrame *F = Frame; F; F = F->Caller) {
1533-
const Function *Func = F->getFunction();
1534-
if (!Func)
1535-
continue;
1536-
const auto *MD = dyn_cast_if_present<CXXMethodDecl>(Func->getDecl());
1537-
if (!MD)
1538-
continue;
1539-
const IdentifierInfo *FnII = MD->getIdentifier();
1540-
if (!FnII || !FnII->isStr("allocate"))
1541-
continue;
1542-
1543-
const auto *CTSD =
1544-
dyn_cast<ClassTemplateSpecializationDecl>(MD->getParent());
1545-
if (!CTSD)
1546-
continue;
1547-
1548-
const IdentifierInfo *ClassII = CTSD->getIdentifier();
1549-
const TemplateArgumentList &TAL = CTSD->getTemplateArgs();
1550-
if (CTSD->isInStdNamespace() && ClassII && ClassII->isStr("allocator") &&
1551-
TAL.size() >= 1 && TAL[0].getKind() == TemplateArgument::Type) {
1552-
ElemType = TAL[0].getAsType();
1553-
NewCall = cast<CallExpr>(F->Caller->getExpr(F->getRetPC()));
1554-
break;
1555-
}
1556-
}
1529+
auto [NewCall, ElemType] = S.getStdAllocatorCaller("allocate");
15571530

15581531
if (ElemType.isNull()) {
15591532
S.FFDiag(Call, S.getLangOpts().CPlusPlus20
@@ -1655,33 +1628,7 @@ static bool interp__builtin_operator_delete(InterpState &S, CodePtr OpPC,
16551628
return false;
16561629

16571630
// This is permitted only within a call to std::allocator<T>::deallocate.
1658-
bool DeallocateFrameFound = false;
1659-
for (const InterpFrame *F = Frame; F; F = F->Caller) {
1660-
const Function *Func = F->getFunction();
1661-
if (!Func)
1662-
continue;
1663-
const auto *MD = dyn_cast_if_present<CXXMethodDecl>(Func->getDecl());
1664-
if (!MD)
1665-
continue;
1666-
const IdentifierInfo *FnII = MD->getIdentifier();
1667-
if (!FnII || !FnII->isStr("deallocate"))
1668-
continue;
1669-
1670-
const auto *CTSD =
1671-
dyn_cast<ClassTemplateSpecializationDecl>(MD->getParent());
1672-
if (!CTSD)
1673-
continue;
1674-
1675-
const IdentifierInfo *ClassII = CTSD->getIdentifier();
1676-
const TemplateArgumentList &TAL = CTSD->getTemplateArgs();
1677-
if (CTSD->isInStdNamespace() && ClassII && ClassII->isStr("allocator") &&
1678-
TAL.size() >= 1 && TAL[0].getKind() == TemplateArgument::Type) {
1679-
DeallocateFrameFound = true;
1680-
break;
1681-
}
1682-
}
1683-
1684-
if (!DeallocateFrameFound) {
1631+
if (!S.getStdAllocatorCaller("deallocate")) {
16851632
S.FFDiag(Call);
16861633
return true;
16871634
}

clang/lib/AST/ByteCode/InterpState.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,33 @@ bool InterpState::maybeDiagnoseDanglingAllocations() {
115115
}
116116
return NoAllocationsLeft;
117117
}
118+
119+
StdAllocatorCaller InterpState::getStdAllocatorCaller(StringRef Name) const {
120+
for (const InterpFrame *F = Current; F; F = F->Caller) {
121+
const Function *Func = F->getFunction();
122+
if (!Func)
123+
continue;
124+
const auto *MD = dyn_cast_if_present<CXXMethodDecl>(Func->getDecl());
125+
if (!MD)
126+
continue;
127+
const IdentifierInfo *FnII = MD->getIdentifier();
128+
if (!FnII || !FnII->isStr(Name))
129+
continue;
130+
131+
const auto *CTSD =
132+
dyn_cast<ClassTemplateSpecializationDecl>(MD->getParent());
133+
if (!CTSD)
134+
continue;
135+
136+
const IdentifierInfo *ClassII = CTSD->getIdentifier();
137+
const TemplateArgumentList &TAL = CTSD->getTemplateArgs();
138+
if (CTSD->isInStdNamespace() && ClassII && ClassII->isStr("allocator") &&
139+
TAL.size() >= 1 && TAL[0].getKind() == TemplateArgument::Type) {
140+
QualType ElemType = TAL[0].getAsType();
141+
const auto *NewCall = cast<CallExpr>(F->Caller->getExpr(F->getRetPC()));
142+
return {NewCall, ElemType};
143+
}
144+
}
145+
146+
return {};
147+
}

clang/lib/AST/ByteCode/InterpState.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ class InterpStack;
3232
class InterpFrame;
3333
class SourceMapper;
3434

35+
struct StdAllocatorCaller {
36+
const Expr *Call = nullptr;
37+
QualType AllocType;
38+
explicit operator bool() { return Call; }
39+
};
40+
3541
/// Interpreter context.
3642
class InterpState final : public State, public SourceMapper {
3743
public:
@@ -116,6 +122,8 @@ class InterpState final : public State, public SourceMapper {
116122
/// \c true otherwise.
117123
bool maybeDiagnoseDanglingAllocations();
118124

125+
StdAllocatorCaller getStdAllocatorCaller(StringRef Name) const;
126+
119127
private:
120128
friend class EvaluationResult;
121129
friend class InterpStateCCOverride;

clang/test/AST/ByteCode/c.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
// RUN: %clang_cc1 -triple x86_64-linux -fexperimental-new-constant-interpreter -verify=expected,all -std=c11 -Wcast-qual %s
2-
// RUN: %clang_cc1 -triple x86_64-linux -fexperimental-new-constant-interpreter -pedantic -verify=pedantic,pedantic-expected,all -std=c11 -Wcast-qual %s
3-
// RUN: %clang_cc1 -triple x86_64-linux -verify=ref,all -std=c11 -Wcast-qual %s
4-
// RUN: %clang_cc1 -triple x86_64-linux -pedantic -verify=pedantic,pedantic-ref,all -std=c11 -Wcast-qual %s
1+
// RUN: %clang_cc1 -triple x86_64-linux -verify=expected,all -std=c11 -Wcast-qual %s -fexperimental-new-constant-interpreter
2+
// RUN: %clang_cc1 -triple x86_64-linux -verify=pedantic,pedantic-expected,all -std=c11 -Wcast-qual -pedantic %s -fexperimental-new-constant-interpreter
3+
// RUN: %clang_cc1 -triple x86_64-linux -verify=ref,all -std=c11 -Wcast-qual %s
4+
// RUN: %clang_cc1 -triple x86_64-linux -verify=pedantic,pedantic-ref,all -std=c11 -Wcast-qual -pedantic %s
55

66
typedef __INTPTR_TYPE__ intptr_t;
77
typedef __PTRDIFF_TYPE__ ptrdiff_t;
@@ -231,7 +231,8 @@ int castViaInt[*(int*)(unsigned long)"test"]; // ref-error {{variable length arr
231231
// expected-error {{variable length array}} \
232232
// pedantic-expected-error {{variable length array}}
233233

234-
const void (*const funcp)(void) = (void*)123; // pedantic-warning {{converts between void pointer and function pointer}}
234+
const void (*const funcp)(void) = (void*)123; // pedantic-warning {{converts between void pointer and function pointer}} \
235+
// pedantic-expected-note {{this conversion is not allowed in a constant expression}}
235236
_Static_assert(funcp == (void*)0, ""); // all-error {{failed due to requirement 'funcp == (void *)0'}} \
236237
// pedantic-warning {{expression is not an integer constant expression}}
237238
_Static_assert(funcp == (void*)123, ""); // pedantic-warning {{equality comparison between function pointer and void pointer}} \

clang/test/AST/ByteCode/cxx11.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,5 +191,6 @@ namespace DynamicCast {
191191
constexpr S* sptr = &s;
192192
struct Str {
193193
int b : reinterpret_cast<S*>(sptr) == reinterpret_cast<S*>(sptr);
194+
int g : (S*)(void*)(sptr) == sptr;
194195
};
195196
}

clang/test/AST/ByteCode/cxx23.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,3 +316,9 @@ namespace ZeroSizedArray {
316316
}
317317
static_assert(foo() == 1);
318318
}
319+
namespace VoidCast {
320+
constexpr int a = 12;
321+
constexpr const int *b = &a;
322+
constexpr int *f = (int*)(void*)b; // all-error {{must be initialized by a constant expression}} \
323+
// all-note {{cast from 'void *' is not allowed in a constant expression}}
324+
}

clang/test/AST/ByteCode/cxx26.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,8 @@ namespace ReplaceableAlloc {
3131
static_assert(foo()); // both-error {{not an integral constant expression}} \
3232
// both-note {{in call to}}
3333
}
34+
35+
constexpr int a = 12;
36+
constexpr const int *b = &a;
37+
constexpr int *f = (int*)(void*)b;
38+
static_assert(*f == 12);

0 commit comments

Comments
 (0)