Skip to content

Commit e44784f

Browse files
authored
[clang][bytecode] Fix pseudo dtor calls on non-pointers (#153970)
The isGLValue() check made us ignore expressions we shouldn't ignore.
1 parent ea4325f commit e44784f

File tree

2 files changed

+45
-1
lines changed

2 files changed

+45
-1
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5146,7 +5146,8 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
51465146
if (!this->emitCheckPseudoDtor(E))
51475147
return false;
51485148
const Expr *Base = PD->getBase();
5149-
if (!Base->isGLValue())
5149+
// E.g. `using T = int; 0.~T();`.
5150+
if (OptPrimType BaseT = classify(Base); !BaseT || BaseT != PT_Ptr)
51505151
return this->discard(Base);
51515152
if (!this->visit(Base))
51525153
return false;

clang/test/AST/ByteCode/builtin-functions.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,27 @@
2121
#error "huh?"
2222
#endif
2323

24+
25+
inline constexpr void* operator new(__SIZE_TYPE__, void* p) noexcept { return p; }
26+
namespace std {
27+
using size_t = decltype(sizeof(0));
28+
template<typename T> struct allocator {
29+
constexpr T *allocate(size_t N) {
30+
return (T*)__builtin_operator_new(sizeof(T) * N); // #alloc
31+
}
32+
constexpr void deallocate(void *p, __SIZE_TYPE__) {
33+
__builtin_operator_delete(p);
34+
}
35+
};
36+
template<typename T, typename... Args>
37+
constexpr T* construct_at(T* p, Args&&... args) { return ::new((void*)p) T(static_cast<Args&&>(args)...); }
38+
39+
template<typename T>
40+
constexpr void destroy_at(T* p) {
41+
p->~T();
42+
}
43+
}
44+
2445
extern "C" {
2546
typedef decltype(sizeof(int)) size_t;
2647
extern size_t wcslen(const wchar_t *p);
@@ -1767,6 +1788,28 @@ namespace WithinLifetime {
17671788
}
17681789
} xstd; // both-error {{is not a constant expression}} \
17691790
// both-note {{in call to}}
1791+
1792+
consteval bool test_dynamic(bool read_after_deallocate) {
1793+
std::allocator<int> a;
1794+
int* p = a.allocate(1);
1795+
// a.allocate starts the lifetime of an array,
1796+
// the complete object of *p has started its lifetime
1797+
if (__builtin_is_within_lifetime(p))
1798+
return false;
1799+
std::construct_at(p);
1800+
if (!__builtin_is_within_lifetime(p))
1801+
return false;
1802+
std::destroy_at(p);
1803+
if (__builtin_is_within_lifetime(p))
1804+
return false;
1805+
a.deallocate(p, 1);
1806+
if (read_after_deallocate)
1807+
__builtin_is_within_lifetime(p); // both-note {{read of heap allocated object that has been deleted}}
1808+
return true;
1809+
}
1810+
static_assert(test_dynamic(false));
1811+
static_assert(test_dynamic(true)); // both-error {{not an integral constant expression}} \
1812+
// both-note {{in call to}}
17701813
}
17711814

17721815
#ifdef __SIZEOF_INT128__

0 commit comments

Comments
 (0)