Created attachment 39481 [details] Full preprocessor output using -save-temps when compiling Preprocessed .ii file is attached. The bug is most easily reproduced using -fsanitize=address option (also running the program under valgrind). I compiled with g++ --std=c++11 -fsanitize=address -o test gcc-bug.cc and then ran: ./test Curiously, the bug only happens when class LambdaHolder is templated. If you remove the template <class U> line and remove <int> from the instantiation line, then the bug is gone. Output of gcc -v: Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper Target: x86_64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.4-2ubuntu1~14.04.3' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu Thread model: posix gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.3)
*** Bug 77313 has been marked as a duplicate of this bug. ***
I don't see why you think this is valid thing to do. You are in a function which calls delete on the object you are in currently. That seems like this code is broken.
(In reply to Andrew Pinski from comment #2) > I don't see why you think this is valid thing to do. > You are in a function which calls delete on the object you are in currently. > That seems like this code is broken. It's valid as long as nothing accesses any members of the class after it is destroyed. When using code you don't control, such as std::function and a lambda closure, you can't guarantee what it does or doesn't do with the memory.
Reduced: struct function_impl_base { virtual ~function_impl_base() = default; virtual void invoke() = 0; }; template<typename F> struct function_impl : function_impl_base { function_impl(F f) : f(f) { } void invoke() { f(); } F f; }; struct function { template<typename F> function(F f) : impl(new function_impl<F>{f}) { } ~function() { delete impl; } function(const function&) = delete; function_impl_base* impl; void operator()() { impl->invoke(); } }; template <class U> struct LambdaHolder { LambdaHolder() : lambda_{[this]() { delete this; }} { } void Run() { lambda_(); } function lambda_; }; int main() { LambdaHolder<int>* l = new LambdaHolder<int>(); l->Run(); } There's no reason this should need to access the freed memory. Clang and EDG don't.
It all comes down to this statement: delete this; The C++ front-end expands it as: [t33.cc:28:39] D.3592 = [t33.cc:28:39] __closure->__this; [t33.cc:28:41] LambdaHolder<int>::~LambdaHolder (D.3592); [t33.cc:28:39] D.3592 = [t33.cc:28:39] __closure->__this; [t33.cc:28:41] operator delete (D.3592, 360); Notice how it reads from __closure here but __closure is deleted during the call to ~LambdaHolder. So the question comes what is the semantics of: delete XYZ; Inside a lambda where XYZ is captured? Do we need to read from the closure twice, once for the deconstructor and then again for the call to delete.
(In reply to Andrew Pinski from comment #5) > Inside a lambda where XYZ is captured? Do we need to read from the closure > twice, once for the deconstructor and then again for the call to delete. I don't see why that should be necessary.
This is fixed in GCC 8: if (SAVE_EXPR <(struct LambdaHolder *) this> != 0B) { try { LambdaHolder<int>::~LambdaHolder (SAVE_EXPR <(struct LambdaHolder *) this>); } finally { operator delete ((void *) SAVE_EXPR <(struct LambdaHolder *) this>, 8); } } else { <<< Unknown tree: void_cst >>> } >>>>>;
Fixed.
Fixed by r251433 "Reimplement handling of lambdas in templates."