On Wed, Aug 9, 2017 at 3:20 PM, Jason Merrill <ja...@redhat.com> wrote: > In this testcase, when building up an extra version of N to refer to > when instantiating the generic lambda, we were mistakenly replacing > the 'auto' with a template argument for the generic lambda. > > Tested x86_64-pc-linux-gnu, applying to trunk and 7.
Recent testing found a bug in this patch, whereby we would mistakenly clobber the TREE_VEC_LENGTH of a single-level argument set, or increase the length rather than decreasing it. Fixed by using strip_innermost_template_args and calculating how many levels we actually want to remove. On the trunk I've also added an assert that we shouldn't get into this situation anymore. Tested x86_64-pc-linux-gnu, applying to trunk and 7.
commit 2e694fe9477fefb4d746943c6996785109f61aa6 Author: Jason Merrill <ja...@redhat.com> Date: Thu Sep 28 16:07:34 2017 -0400 PR c++/81525 - broken handling of auto in generic lambda. * pt.c (tsubst_decl) [VAR_DECL]: Use strip_innermost_template_args. diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index f3ad6083190..38d7b45eb9b 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -12896,15 +12896,17 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain) && VAR_HAD_UNKNOWN_BOUND (t) && type != error_mark_node) type = strip_array_domain (type); - tree auto_node = type_uses_auto (type); - int len = TREE_VEC_LENGTH (args); - if (auto_node) - /* Mask off any template args past the variable's context so we - don't replace the auto with an unrelated argument. */ - TREE_VEC_LENGTH (args) = TEMPLATE_TYPE_LEVEL (auto_node) - 1; - type = tsubst (type, args, complain, in_decl); - if (auto_node) - TREE_VEC_LENGTH (args) = len; + tree sub_args = args; + if (tree auto_node = type_uses_auto (type)) + { + /* Mask off any template args past the variable's context so we + don't replace the auto with an unrelated argument. */ + int nouter = TEMPLATE_TYPE_LEVEL (auto_node) - 1; + int extra = TMPL_ARGS_DEPTH (args) - nouter; + if (extra > 0) + sub_args = strip_innermost_template_args (args, extra); + } + type = tsubst (type, sub_args, complain, in_decl); } if (VAR_P (r)) { diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-auto1.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-auto1.C new file mode 100644 index 00000000000..b9e98c551c0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-auto1.C @@ -0,0 +1,19 @@ +// Related to c++/81525 +// { dg-do compile { target c++14 } } + +template <class X> +struct A +{ + template <class T> + static void f() + { + [](auto b) { + auto c = +b; + }(42); + } +}; + +int main() +{ + A<int>::f<int>(); +}
commit c6c77e3c274bc8a1bdd33be58895e1ae08a453a7 Author: Jason Merrill <ja...@redhat.com> Date: Thu Sep 28 16:07:34 2017 -0400 PR c++/81525 - broken handling of auto in generic lambda. * pt.c (tsubst_decl) [VAR_DECL]: Use strip_innermost_template_args. diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index c29c779a147..36c8c106439 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -13042,15 +13042,20 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain) && VAR_HAD_UNKNOWN_BOUND (t) && type != error_mark_node) type = strip_array_domain (type); - tree auto_node = type_uses_auto (type); - int len = TREE_VEC_LENGTH (args); - if (auto_node) - /* Mask off any template args past the variable's context so we - don't replace the auto with an unrelated argument. */ - TREE_VEC_LENGTH (args) = TEMPLATE_TYPE_LEVEL (auto_node) - 1; - type = tsubst (type, args, complain, in_decl); - if (auto_node) - TREE_VEC_LENGTH (args) = len; + tree sub_args = args; + if (tree auto_node = type_uses_auto (type)) + { + /* Mask off any template args past the variable's context so we + don't replace the auto with an unrelated argument. */ + int nouter = TEMPLATE_TYPE_LEVEL (auto_node) - 1; + int extra = TMPL_ARGS_DEPTH (args) - nouter; + if (extra > 0) + /* This should never happen with the new lambda instantiation + model, but keep the handling just in case. */ + gcc_assert (!CHECKING_P), + sub_args = strip_innermost_template_args (args, extra); + } + type = tsubst (type, sub_args, complain, in_decl); } if (VAR_P (r)) { diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-auto1.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-auto1.C new file mode 100644 index 00000000000..b9e98c551c0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-auto1.C @@ -0,0 +1,19 @@ +// Related to c++/81525 +// { dg-do compile { target c++14 } } + +template <class X> +struct A +{ + template <class T> + static void f() + { + [](auto b) { + auto c = +b; + }(42); + } +}; + +int main() +{ + A<int>::f<int>(); +} diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4.C index d56f379c680..1cf85518813 100644 --- a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4.C +++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4.C @@ -1,5 +1,5 @@ // PR c++/81525 -// { dg-do compile { target c++14 } } +// { dg-do run { target c++14 } } template <int i> struct A { constexpr operator int () const { return i; } @@ -13,7 +13,7 @@ template <typename T> void bar (T) { constexpr auto N = a<1>; auto f = [&] (auto i) { - return static_cast<int>(N) == 1; + if (static_cast<int>(N) != 1) __builtin_abort(); }; foo (f); } diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4a.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4a.C new file mode 100644 index 00000000000..a6afb32584f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4a.C @@ -0,0 +1,20 @@ +// PR c++/81525 +// { dg-do run { target c++14 } } + +template <int i> struct A { + constexpr operator int () const { return i; } +}; +template <int i> constexpr A<i> a = {}; + +template <typename F> void foo (F f) { + f (42); +} +template <typename T> +void bar (T) { + constexpr auto N = a<1>; + auto f = [&] (auto i) { + if (static_cast<decltype(i)>(N) != 1) __builtin_abort(); + }; + foo (f); +} +int main () { bar (0); }