kristina updated this revision to Diff 173503. kristina set the repository for this revision to rC Clang. kristina added a comment.
Revised, I think I worked out what was happening, there's an explanation in the comment in the test. This is a relatively new attribute so the coverage for it did not test this specific corner case, I've managed to manually narrow it down and write a reliable regression test. The core issue boils down to having a non-trivially destructible member in a superclass that lacks a destructor and a subclass that lacks a destructor, in which case, a different path was taken to balance out the static storage duration class' initializer call and the `__cxa_atexit` registration. This adds an explicit check for the attribute and avoids balancing out the constructor as intended by the attribute. The new test successfully replicates the assertion failure and should fail for the above cases in assertion builds. Without assertions the generated code produces undefined behavior if the above conditions are met. There is a separate test for this attribute that provides the coverage for its functionality, this is a much more narrower test for a very specific scenario in which it was possible to cause an improperly balanced constructor call followed by a emission of a call to a destructor that would never be emitted due to the attribute, thus tripping the assert. Now no attempt to call the destructor is made if the declaration is marked as `no_destroy`. `Test without patch:` => ninja check-clang-semacxx FAIL: Clang :: SemaCXX/attr-no-destroy-d54344.cpp (164 of 818) ******************** TEST 'Clang :: SemaCXX/attr-no-destroy-d54344.cpp' FAILED ******************** (--- stack trace and the expected assertion failure ---) /q/org.llvm.caches/llvm-8.0/4141/tools/clang/test/SemaCXX/Output/attr-no-destroy-d54344.cpp.script: line 1: 31356 Aborted (core dumped) -- ******************** Testing Time: 5.03s ******************** Failing Tests (1): Clang :: SemaCXX/attr-no-destroy-d54344.cpp Expected Passes : 816 Expected Failures : 1 Unexpected Failures: 1 FAILED: tools/clang/test/CMakeFiles/check-clang-semacxx ninja: build stopped: subcommand failed. `Patch applied:` => ninja check-clang-semacxx Testing Time: 6.28s Expected Passes : 817 Expected Failures : 1 Repository: rC Clang https://reviews.llvm.org/D54344 Files: lib/CodeGen/CGDeclCXX.cpp test/SemaCXX/attr-no-destroy-d54344.cpp
Index: test/SemaCXX/attr-no-destroy-d54344.cpp =================================================================== --- test/SemaCXX/attr-no-destroy-d54344.cpp +++ test/SemaCXX/attr-no-destroy-d54344.cpp @@ -0,0 +1,92 @@ +// RUN: %clang_cc1 -std=c++2a -emit-llvm -S -O0 %s -o - > /dev/null +// REQUIRES: asserts + +// Regression test for D54344. Class with no user-defined destructor +// that has an inherited member that has a non-trivial destructor +// and a non-default constructor will attempt to emit a destructor +// despite being marked as __attribute((no_destroy)) in which case +// it would trigger an assertion due to an incorrect assumption. +// This test requires asserts to reliably work as without the crash +// it generates working but semantically incorrect code. + +namespace util { + template <typename T> + class test_vector + { + public: + test_vector(unsigned c) + : Used(0), TotalSize(sizeof(T) * c) + { + // Intentional + Storage = (T*)(new T[TotalSize]); + } + ~test_vector() { + delete[] Storage; + } + char* data() { + return Storage; + } + unsigned size() { + return TotalSize; + } + void push_back(T one_thing) { + Storage[Used++] = one_thing; + } + private: + unsigned Used; + unsigned TotalSize; + T* Storage; + }; +} + +volatile int do_not_optimize_me = 0; + +namespace os { + using char_vec_t = util::test_vector<char>; + + class logger_base + { + public: + constexpr logger_base() = default; + protected: + char_vec_t* NamePtr = nullptr; + char_vec_t Name = char_vec_t(10); + }; + + class logger : public logger_base + { + public: + void stuff(const char* fmt, int something) { + do_not_optimize_me = something + fmt[0]; + } + logger() + { + Name = char_vec_t(8); + Name.push_back('a'); + } + + logger(const char* name) + { + Name = util::test_vector<char>(__builtin_strlen(name)); + Name.push_back(name[0]); + Name.push_back(name[1]); + } + }; +} + +#define TOPLEVEL_LOGGER(_var_name, _category_const) \ + namespace { constexpr char $__##_var_name[] = _category_const; \ + __attribute((no_destroy)) os::logger _var_name ($__##_var_name) ; } + +TOPLEVEL_LOGGER(s_log, "something"); + +class some_class { +public: + some_class(int some_value) { + do_not_optimize_me = some_value; + s_log.stuff("wawawa", 5 + do_not_optimize_me); + } + ~some_class() = default; +}; + +static some_class s_ctor(1); \ No newline at end of file Index: lib/CodeGen/CGDeclCXX.cpp =================================================================== --- lib/CodeGen/CGDeclCXX.cpp +++ lib/CodeGen/CGDeclCXX.cpp @@ -64,6 +64,14 @@ /// static storage duration. static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D, ConstantAddress Addr) { + // Honor __attribute__((no_destroy)) and bail instead of attempting + // to emit a reference to a possibly non-existant destructor, which + // in turn can cause a crash. This will result in a global constructor + // that isn't balanced out by a destructor call as intended by the + // attribute (D54344). + if (D.hasAttr<NoDestroyAttr>()) + return; + CodeGenModule &CGM = CGF.CGM; // FIXME: __attribute__((cleanup)) ?
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits