Bug 79180 - Nested lambda-capture causes segfault for parameter pack
Summary: Nested lambda-capture causes segfault for parameter pack
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 6.3.0
: P3 normal
Target Milestone: 8.0
Assignee: Not yet assigned to anyone
URL:
Keywords: c++-lambda, wrong-code
Depends on:
Blocks: lambdas
  Show dependency treegraph
 
Reported: 2017-01-22 08:25 UTC by Markus Dreseler
Modified: 2022-03-11 00:32 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2017-01-23 00:00:00


Attachments
Preprocessed file (83.61 KB, text/plain)
2017-01-22 08:25 UTC, Markus Dreseler
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Markus Dreseler 2017-01-22 08:25:37 UTC
Created attachment 40561 [details]
Preprocessed file

The following code compiles but prints garbage and then segfaults under gcc 6.3.0 on OS X:

	#include <iostream>
	#include <string>

	void foo(const std::string &a) {
	  std::cout << a << std::endl;
	}

	template <typename... Args>
	void bar(Args &&... args) {
	  [&]() {
	    [&]() {
	          foo(args...);
	      }();
	  }();
	}

	int main() {
	 const std::string x("Hello World!");
	 bar(x);
	}

Expected result: Print "Hello World!" and exit.

Compiling with `-Wextra -Wall` gives this warning:

	param.cpp: In function 'int main()':
	param.cpp:11:11: warning: 'args#0' is used uninitialized in this function [-Wuninitialized]
	           foo(args...);
	           ^~~
	param.cpp:16:5: note: 'args#0' was declared here
	 int main() {
	     ^~~~

Clang and VC++ do not have any issues here. People at StackOverflow seem to agree that this is valid code and appears to be a bug in GCC:
http://stackoverflow.com/questions/41769851/gcc-causes-segfault-for-lambda-captured-parameter-pack

Additional information:
	[:~/tmp] 1 $ g++-6 param.cpp && ./a.out
	# Garbage here
	Segmentation fault: 11

	[:~/tmp] 139 $ g++-6 -v
	Using built-in specs.
	COLLECT_GCC=g++-6
	COLLECT_LTO_WRAPPER=/usr/local/Cellar/gcc/6.3.0_1/libexec/gcc/x86_64-apple-darwin16.3.0/6.3.0/lto-wrapper
	Target: x86_64-apple-darwin16.3.0
	Configured with: ../configure --build=x86_64-apple-darwin16.3.0 --prefix=/usr/local/Cellar/gcc/6.3.0_1 --libdir=/usr/local/Cellar/gcc/6.3.0_1/lib/gcc/6 --enable-languages=c,c++,objc,obj-c++,fortran --program-suffix=-6 --with-gmp=/usr/local/opt/gmp --with-mpfr=/usr/local/opt/mpfr --with-mpc=/usr/local/opt/libmpc --with-isl=/usr/local/opt/isl --with-system-zlib --enable-libstdcxx-time=yes --enable-stage1-checking --enable-checking=release --enable-lto --with-build-config=bootstrap-debug --disable-werror --with-pkgversion='Homebrew GCC 6.3.0_1' --with-bugurl=https://github.com/Homebrew/homebrew-core/issues --enable-plugin --disable-nls --enable-multilib
	Thread model: posix
	gcc version 6.3.0 (Homebrew GCC 6.3.0_1)

	[:~/tmp] 1 $ sw_vers
	ProductName:	Mac OS X
	ProductVersion:	10.12.2
	BuildVersion:	16C67
Comment 1 Markus Dreseler 2017-01-22 08:31:09 UTC
P.S.: Also experienced on Wandbox's GCC 7.0.1 HEAD.

Not passing the parameter pack into bar by universal reference causes an internal compiler error in gcc 7.0.1 HEAD:

	prog.cc: In function 'void bar(Args ...) [with Args = {std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >}]':
	prog.cc:14:4: internal compiler error: in make_decl_rtl, at varasm.c:1304
	   [&]() {
	   ~~~~~~~
	     [&]() {
	     ~~~~~~~
	           foo(args...);
	           ~~~~~~~~~~~~~
	       }();
	       ~~~~
	   }();
	   ~^~



http://melpon.org/wandbox/permlink/mYtVweIn8zv4t74Y

Thanks to AMA at StackOverflow for finding this.
Comment 2 Jakub Jelinek 2017-01-23 10:01:33 UTC
Simplified testcase:
void
foo (int a)
{
  if (a != 127)
    __builtin_abort ();
}

template <typename... Args>
void
bar (Args &&... args)
{
  [&]() { [&]() { foo (args...); } (); } ();
}

int
main ()
{
  int x = 127;
  bar (x);
}
Comment 3 Jakub Jelinek 2017-01-23 10:43:50 UTC
Seems there is a mismatch between what PARM_DECL is used.

E.g. -fdump-tree-omplower-all dump shows:
void bar(Args&& ...) [with Args = {int&}] (intD.9 & args#0D.2308)
{
  struct __lambda0D.2311 D.2341;
  typedef struct __lambda0D.2311 __lambda0D.2311;

  D.2341.__args#0D.2317 = args#0D.2339;
...

One args#0 PARM_DECL, with uid 2308 is created during:
#1  0x00000000014689bf in copy_node_stat (node=<parm_decl 0x7fffefc45080 args>) at ../../gcc/tree.c:1157
#2  0x0000000000823237 in tsubst_decl (t=<parm_decl 0x7fffefc45080 args>, args=<tree_vec 0x7fffefc43c60>, complain=0) at ../../gcc/cp/pt.c:12447
#3  0x0000000000826de5 in tsubst (t=<parm_decl 0x7fffefc45080 args>, args=<tree_vec 0x7fffefc43c60>, complain=0, 
    in_decl=<function_decl 0x7fffefc46100 bar>) at ../../gcc/cp/pt.c:13272
#4  0x000000000082150e in tsubst_decl (t=<function_decl 0x7fffefc46100 bar>, args=<tree_vec 0x7fffefc43c60>, complain=0)
    at ../../gcc/cp/pt.c:12273
#5  0x0000000000826de5 in tsubst (t=<function_decl 0x7fffefc46100 bar>, args=<tree_vec 0x7fffefc43c60>, complain=0, 
    in_decl=<template_decl 0x7fffefc45100 bar>) at ../../gcc/cp/pt.c:13272
#6  0x0000000000841d23 in instantiate_template_1 (tmpl=<template_decl 0x7fffefc45100 bar>, orig_args=<tree_vec 0x7fffefc43c60>, complain=0)
    at ../../gcc/cp/pt.c:18089
#7  0x000000000084231d in instantiate_template (tmpl=<template_decl 0x7fffefc45100 bar>, orig_args=<tree_vec 0x7fffefc43c60>, complain=0)
    at ../../gcc/cp/pt.c:18145
#8  0x0000000000843683 in fn_type_unification (fn=<template_decl 0x7fffefc45100 bar>, explicit_targs=<tree 0x0>, targs=<tree_vec 0x7fffefc43c60>, 
    args=0x7fffffffc8a0, nargs=1, return_type=<tree 0x0>, strict=DEDUCE_CALL, flags=1, explain_p=false, decltype_p=false)
    at ../../gcc/cp/pt.c:18525
#9  0x000000000076f4c2 in add_template_candidate_real (candidates=0x7fffffffcba0, tmpl=<template_decl 0x7fffefc45100 bar>, ctype=<tree 0x0>, 
    explicit_targs=<tree 0x0>, first_arg=<tree 0x0>, arglist=0x7fffefc4a9d8, return_type=<tree 0x0>, access_path=<tree 0x0>, 
    conversion_path=<tree 0x0>, flags=1, obj=<tree 0x0>, strict=DEDUCE_CALL, complain=3) at ../../gcc/cp/call.c:3164
#10 0x000000000076f91f in add_template_candidate (candidates=0x7fffffffcba0, tmpl=<template_decl 0x7fffefc45100 bar>, ctype=<tree 0x0>, 
    explicit_targs=<tree 0x0>, first_arg=<tree 0x0>, arglist=0x7fffefc4a9d8, return_type=<tree 0x0>, access_path=<tree 0x0>, 
    conversion_path=<tree 0x0>, flags=1, strict=DEDUCE_CALL, complain=3) at ../../gcc/cp/call.c:3246
#11 0x0000000000777c8b in add_candidates (fns=<overload 0x7fffefc436e0>, first_arg=<tree 0x0>, args=0x7fffefc4a9d8, return_type=<tree 0x0>, 
    explicit_targs=<tree 0x0>, template_only=false, conversion_path=<tree 0x0>, access_path=<tree 0x0>, flags=1, candidates=0x7fffffffcba0, 
    complain=3) at ../../gcc/cp/call.c:5488
#12 0x0000000000772af7 in perform_overload_resolution (fn=<overload 0x7fffefc436e0>, args=0x7fffefc4a9d8, candidates=0x7fffffffcba0, 
    any_viable_p=0x7fffffffcb9f, complain=3) at ../../gcc/cp/call.c:4142
#13 0x0000000000772de4 in build_new_function_call (fn=<overload 0x7fffefc436e0>, args=0x7fffffffcd20 = {...}, koenig_p=true, complain=3)
    at ../../gcc/cp/call.c:4227
#14 0x00000000009a7943 in finish_call_expr (fn=<overload 0x7fffefc436e0>, args=0x7fffffffcd20 = {...}, disallow_virtual=false, koenig_p=true, 
    complain=3) at ../../gcc/cp/semantics.c:2441
#15 0x00000000008e5475 in cp_parser_postfix_expression (parser=0x7ffff7ff5bd0, address_p=false, cast_p=false, member_access_only_p=false, 
    decltype_p=false, pidk_return=0x0) at ../../gcc/cp/parser.c:7019

i.e. on:
12273		DECL_ARGUMENTS (r) = tsubst (DECL_ARGUMENTS (t), args,
12274					     complain, t);

The other args#0 PARM_DECL is created during:
#1  0x00000000014689bf in copy_node_stat (node=<parm_decl 0x7fffefc45080 args>) at ../../gcc/tree.c:1157
#2  0x0000000000823237 in tsubst_decl (t=<parm_decl 0x7fffefc45080 args>, args=<tree_vec 0x7fffefc54020>, complain=3) at ../../gcc/cp/pt.c:12447
#3  0x000000000081c3aa in tsubst_pack_expansion (t=<expr_pack_expansion 0x7fffefabedb0>, args=<tree_vec 0x7fffefc54020>, complain=3, 
    in_decl=<tree 0x0>) at ../../gcc/cp/pt.c:11313
#4  0x000000000083df90 in tsubst_copy_and_build (t=<tree_list 0x7fffefc4a988>, args=<tree_vec 0x7fffefc54020>, complain=3, in_decl=<tree 0x0>, 
    function_p=false, integral_constant_expression_p=false) at ../../gcc/cp/pt.c:17384
#5  0x000000000081a380 in instantiate_class_template_1 (type=<record_type 0x7fffefc533f0 __lambda0>) at ../../gcc/cp/pt.c:10691
#6  0x000000000081a685 in instantiate_class_template (type=<record_type 0x7fffefc533f0 __lambda0>) at ../../gcc/cp/pt.c:10749
#7  0x000000000093ebad in complete_type (type=<record_type 0x7fffefc533f0 __lambda0>) at ../../gcc/cp/typeck.c:133
#8  0x000000000084068f in tsubst_copy_and_build (t=<lambda_expr 0x7fffefaea050>, args=<tree_vec 0x7fffefc43c60>, complain=3, 
    in_decl=<template_decl 0x7fffefc45100 bar>, function_p=true, integral_constant_expression_p=false) at ../../gcc/cp/pt.c:17800
#9  0x000000000083c0d0 in tsubst_copy_and_build (t=<call_expr 0x7fffefc51000>, args=<tree_vec 0x7fffefc43c60>, complain=3, 
    in_decl=<template_decl 0x7fffefc45100 bar>, function_p=false, integral_constant_expression_p=false) at ../../gcc/cp/pt.c:17077
#10 0x0000000000838e48 in tsubst_expr (t=<call_expr 0x7fffefc51000>, args=<tree_vec 0x7fffefc43c60>, complain=3, 
    in_decl=<template_decl 0x7fffefc45100 bar>, integral_constant_expression_p=false) at ../../gcc/cp/pt.c:16403
#11 0x0000000000833445 in tsubst_expr (t=<expr_stmt 0x7fffefc43c00>, args=<tree_vec 0x7fffefc43c60>, complain=3, 
    in_decl=<template_decl 0x7fffefc45100 bar>, integral_constant_expression_p=false) at ../../gcc/cp/pt.c:15671
#12 0x000000000083332a in tsubst_expr (t=<statement_list 0x7fffefc43580>, args=<tree_vec 0x7fffefc43c60>, complain=3, 
    in_decl=<template_decl 0x7fffefc45100 bar>, integral_constant_expression_p=false) at ../../gcc/cp/pt.c:15657
#13 0x0000000000834f77 in tsubst_expr (t=<bind_expr 0x7fffefabeb70>, args=<tree_vec 0x7fffefc43c60>, complain=3, 
    in_decl=<template_decl 0x7fffefc45100 bar>, integral_constant_expression_p=false) at ../../gcc/cp/pt.c:15881
#14 0x0000000000856ecc in instantiate_decl (d=<function_decl 0x7fffefc46500 bar>, defer_ok=0, expl_inst_class_mem_p=false)
    at ../../gcc/cp/pt.c:22812

So when we use a PARM_DECL in the initialization of the lambda struct that is actually not tha containing function's PARM_DECL, it is uninitialized use.
Comment 4 Vittorio Romeo 2017-07-15 15:18:00 UTC
Experienced this bug today. Another example + information available here:
https://stackoverflow.com/questions/45117719

Possibly related:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71386
Comment 5 paolo@gcc.gnu.org 2017-10-02 12:40:57 UTC
Author: paolo
Date: Mon Oct  2 12:40:26 2017
New Revision: 253350

URL: https://gcc.gnu.org/viewcvs?rev=253350&root=gcc&view=rev
Log:
2017-10-02  Paolo Carlini  <paolo.carlini@oracle.com>

	PR c++/79180
	* g++.dg/cpp0x/lambda/lambda-nested8.C: New.
	* g++.dg/torture/pr79180.C: Likewise.

	PR c++/71386
	* g++.dg/cpp1y/lambda-generic-nested1.C: New.

Added:
    trunk/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-nested8.C
    trunk/gcc/testsuite/g++.dg/cpp1y/lambda-generic-nested1.C
    trunk/gcc/testsuite/g++.dg/torture/pr79180.C
Modified:
    trunk/gcc/testsuite/ChangeLog
Comment 6 Paolo Carlini 2017-10-02 12:48:51 UTC
Fixed in trunk, I added testcases.