Skip to content

Commit b80b68a

Browse files
vtjnashIcohedron
authored andcommitted
[analyzer] Do not destruct fields of unions (llvm#122330)
The C++ standard prohibits this implicit destructor call, leading to incorrect reports from clang-analyzer. This causes projects that use std::option (including llvm) to fail the cplusplus.NewDelete test incorrectly when run through the analyzer. Fixes llvm#119415
1 parent 56f9300 commit b80b68a

File tree

4 files changed

+69
-0
lines changed

4 files changed

+69
-0
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ Improvements to Clang's diagnostics
127127
- The ``-Wunique-object-duplication`` warning has been added to warn about objects
128128
which are supposed to only exist once per program, but may get duplicated when
129129
built into a shared library.
130+
- Fixed a bug where Clang's Analysis did not correctly model the destructor behavior of ``union`` members (#GH119415).
130131

131132
Improvements to Clang's time-trace
132133
----------------------------------

clang/lib/Analysis/CFG.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2041,6 +2041,8 @@ void CFGBuilder::addImplicitDtorsForDestructor(const CXXDestructorDecl *DD) {
20412041
}
20422042

20432043
// First destroy member objects.
2044+
if (RD->isUnion())
2045+
return;
20442046
for (auto *FI : RD->fields()) {
20452047
// Check for constant size array. Set type to array element type.
20462048
QualType QT = FI->getType();

clang/test/Analysis/NewDelete-checker-test.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,3 +441,31 @@ void testLeakBecauseNTTPIsNotDeallocation() {
441441
void* p = ::operator new(10);
442442
deallocate_via_nttp<not_free>(p);
443443
} // leak-warning{{Potential leak of memory pointed to by 'p'}}
444+
445+
namespace optional_union {
446+
template <typename T>
447+
class unique_ptr {
448+
T *q;
449+
public:
450+
unique_ptr() : q(new T) {}
451+
~unique_ptr() {
452+
delete q;
453+
}
454+
};
455+
456+
union custom_union_t {
457+
unique_ptr<int> present;
458+
char notpresent;
459+
custom_union_t() : present(unique_ptr<int>()) {}
460+
~custom_union_t() {}
461+
};
462+
463+
void testUnionCorrect() {
464+
custom_union_t a;
465+
a.present.~unique_ptr<int>();
466+
}
467+
468+
void testUnionLeak() {
469+
custom_union_t a;
470+
} // leak-warning{{Potential leak of memory pointed to by 'a.present.q'}}
471+
}

clang/test/Analysis/dtor-union.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-inlining=destructors -verify -std=c++11 %s
2+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-inlining=destructors -verify -std=c++17 %s
3+
4+
void clang_analyzer_eval(bool);
5+
6+
struct InlineDtor {
7+
static int cnt;
8+
static int dtorCalled;
9+
~InlineDtor() {
10+
++dtorCalled;
11+
}
12+
};
13+
14+
int InlineDtor::cnt = 0;
15+
int InlineDtor::dtorCalled = 0;
16+
17+
void testUnionDtor() {
18+
static int unionDtorCalled;
19+
InlineDtor::cnt = 0;
20+
InlineDtor::dtorCalled = 0;
21+
unionDtorCalled = 0;
22+
{
23+
union UnionDtor {
24+
InlineDtor kind1;
25+
char kind2;
26+
~UnionDtor() { unionDtorCalled++; }
27+
};
28+
UnionDtor u1{.kind1{}};
29+
UnionDtor u2{.kind2{}};
30+
auto u3 = new UnionDtor{.kind1{}};
31+
auto u4 = new UnionDtor{.kind2{}};
32+
delete u3;
33+
delete u4;
34+
}
35+
36+
clang_analyzer_eval(unionDtorCalled == 4); // expected-warning {{TRUE}}
37+
clang_analyzer_eval(InlineDtor::dtorCalled == 0); // expected-warning {{TRUE}}
38+
}

0 commit comments

Comments
 (0)