The valid C++11 program below segfaults on my machine: ===== #include <cassert> #include <cstring> #include <iterator> void test(const char* s) { constexpr /*static*/ struct {const char* s;} ar[]={ {"e4"}, {"e5"}, {"Nf3"}, {"Nc6"}, {"Bb5"}, {"a6"} }; auto it=std::begin(ar); auto compare=[&it](const char* s) { assert(std::strcmp(it->s,s)==0); }; auto test=[&](const char* s) { it=std::begin(ar); compare(s); }; test(s); } int main(int argc, const char* argv[]) { test(argv[1]); test(argv[2]); test(argv[3]); return 0; } ===== I compile with: $ g++ -g -O --std=c++11 bug.cc. I run as: $ ./a.out e4 e4 e4 My machine is a 64 bit linux intel core duo 2 (T4200). If I make "ar" static the program passes, so I assume that ar is overwritten prematurely on the stack. The error only occurs from optimization-levels "-O" onwards.
Created attachment 29909 [details] preprocessed file
the output of gcc -v Using built-in specs. COLLECT_GCC=g++-4.8.0 COLLECT_LTO_WRAPPER=/home/dirk/local/libexec/gcc/x86_64-unknown-linux-gnu/4.8.0/lto-wrapper Target: x86_64-unknown-linux-gnu Configured with: ./configure --prefix=/home/dirk/local --program-suffix=-4.8.0 --enable-languages=c,c++ Thread model: posix gcc version 4.8.0 (GCC)
I still see the crash with GCC 6 (the program runs fine when compiled with Clang). $ /build/gcc-6-branch/gcc/xg++ -B /build/gcc-6-branch/gcc -nostdinc++ -I /build/gcc-6-branch/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu -I /build/gcc-6-branch/x86_64-pc-linux-gnu/libstdc++-v3/include -I /src/gcc/6-branch/libstdc++-v3/libsupc++ -I /src/gcc/6-branch/libstdc++-v3/include/backward -I /src/gcc/6-branch/libstdc++-v3/testsuite/util -L /build/gcc-6-branch/x86_64-pc-linux-gnu/libstdc++-v3/src/.libs -O1 -g t.C && gdb -q -ex 'r e4 e4 e4' -ex bt -ex 'x/i $pc' -ex 'p $rdi' ./a.out Reading symbols from ./a.out...done. Starting program: /home/msebor/build/tmp/a.out e4 e4 e4 Program received signal SIGSEGV, Segmentation fault. 0x00007ffff7336266 in __strcmp_ssse3 () from /lib64/libc.so.6 #0 0x00007ffff7336266 in __strcmp_ssse3 () from /lib64/libc.so.6 #1 0x0000000000400656 in operator() (s=<optimized out>, __closure=<synthetic pointer>) at t.C:19 #2 operator() (s=<optimized out>, __closure=<synthetic pointer>) at t.C:25 #3 test (s=<optimized out>) at t.C:28 #4 0x0000000000400685 in main (argc=<optimized out>, argv=0x7fffffffdf38) at t.C:33 => 0x7ffff7336266 <__strcmp_ssse3+22>: movlpd (%rdi),%xmm1 $1 = 1 GCC 7 fails to compile the test case with the errors below: t.C: In lambda function: t.C:24:26: error: no matching function for call to ‘begin(const test(const char*)::<unnamed struct> [6])’ it=std::begin(ar); ^ In file included from /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/range_access.h:36:0, from /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/string:51, from /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/locale_classes.h:40, from /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/ios_base.h:41, from /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/ios:42, from /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/ostream:38, from /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/iterator:64, from t.C:3: /src/gcc/svn/libstdc++-v3/libsupc++/initializer_list:89:5: note: candidate: template<class _Tp> constexpr const _Tp* std::begin(std::initializer_list<_Tp>) begin(initializer_list<_Tp> __ils) noexcept ^~~~~ /src/gcc/svn/libstdc++-v3/libsupc++/initializer_list:89:5: note: template argument deduction/substitution failed: t.C:24:26: note: mismatched types ‘std::initializer_list<_Tp>’ and ‘const test(const char*)::<unnamed struct>*’ it=std::begin(ar); ^ In file included from /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/string:51:0, from /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/locale_classes.h:40, from /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/ios_base.h:41, from /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/ios:42, from /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/ostream:38, from /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/iterator:64, from t.C:3: /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/range_access.h:48:5: note: candidate: template<class _Container> decltype (__cont.begin()) std::begin(_Container&) begin(_Container& __cont) -> decltype(__cont.begin()) ^~~~~ /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/range_access.h:48:5: note: template argument deduction/substitution failed: /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/range_access.h: In substitution of ‘template<class _Container> decltype (__cont.begin()) std::begin(_Container&) [with _Container = const test(const char*)::<unnamed struct> [6]]’: t.C:24:26: required from here /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/range_access.h:48:50: error: request for member ‘begin’ in ‘__cont’, which is of non-class type ‘const test(const char*)::<unnamed struct> [6]’ begin(_Container& __cont) -> decltype(__cont.begin()) ~~~~~~~^~~~~ /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/range_access.h:58:5: note: candidate: template<class _Container> decltype (__cont.begin()) std::begin(const _Container&) begin(const _Container& __cont) -> decltype(__cont.begin()) ^~~~~ /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/range_access.h:58:5: note: template argument deduction/substitution failed: /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/range_access.h: In substitution of ‘template<class _Container> decltype (__cont.begin()) std::begin(const _Container&) [with _Container = test(const char*)::<unnamed struct> [6]]’: t.C:24:26: required from here /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/range_access.h:58:56: error: request for member ‘begin’ in ‘__cont’, which is of non-class type ‘const test(const char*)::<unnamed struct> [6]’ begin(const _Container& __cont) -> decltype(__cont.begin()) ~~~~~~~^~~~~ /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/range_access.h:87:5: note: candidate: _Tp* std::begin(_Tp (&)[_Nm]) [with _Tp = const test(const char*)::<unnamed struct>; long unsigned int _Nm = 6] begin(_Tp (&__arr)[_Nm]) ^~~~~ /build/gcc-svn/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/range_access.h:87:5: note: no known conversion for argument 1 from ‘const test(const char*)::<unnamed struct> [6]’ to ‘const test(const char*)::<unnamed struct> (&)[6]’
G++ still implements an early proposal for DR 696, whereby a reference to a constant automatic variable from a lambda is replaced by the value of the variable rather than implying a capture. We need to properly implement DR 696 such that odr-use implies a capture.
Nathan mentioned today that we have the inverse problem as well: after we've captured i, we should still be able to use its constant value. Here's an example of things that ought to work and mostly don't: int main() { const int i = 4; [] { constexpr int x = i; }; [=] { &i; constexpr int x = i; }; [&] { &i; constexpr int x = i; }; [i] { &i; constexpr int x = i; }; [&i] { &i; constexpr int x = i; }; } "Every id-expression within the compound-statement of a lambda-expression that is an odr-use (6.2) of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type. [ Note: An id-expression that is not an odr-use refers to the original entity, never to a member of the closure type. Furthermore, such an id-expression does not cause the implicit capture of the entity. — end note ]" "A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (7.1) to x yields a constant expression (8.20) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (7.1) is applied to e, or e is a discarded-value expression (Clause 8)."
Author: jason Date: Thu Sep 28 19:39:45 2017 New Revision: 253266 URL: https://gcc.gnu.org/viewcvs?rev=253266&root=gcc&view=rev Log: PR c++/56973, DR 696 - capture constant variables only as needed. * expr.c (mark_use): Split out from mark_rvalue_use and mark_lvalue_use. Handle lambda capture of constant variables. (mark_lvalue_use_nonread): New. * semantics.c (process_outer_var_ref): Don't capture a constant variable until forced. * pt.c (processing_nonlambda_template): New. * call.c (build_this): Check it. * decl2.c (grok_array_decl): Call mark_rvalue_use and mark_lvalue_use_nonread. * init.c (constant_value_1): Don't call mark_rvalue_use. * typeck.c (build_static_cast): Handle lambda capture. Added: trunk/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const6.C trunk/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const7.C Modified: trunk/gcc/cp/ChangeLog trunk/gcc/cp/call.c trunk/gcc/cp/cp-tree.h trunk/gcc/cp/decl2.c trunk/gcc/cp/expr.c trunk/gcc/cp/init.c trunk/gcc/cp/pt.c trunk/gcc/cp/semantics.c trunk/gcc/cp/typeck.c trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-64462.C trunk/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4.C
Jason, can we resolve this? I didn't investigate much but I see [DR 696] in the subject and in your last patch, isn't obvious what is still missing?!?
Yes, this is fixed now.
*** Bug 79104 has been marked as a duplicate of this bug. ***