Re: [C++ PATCH] pr 67273 & 79253 lambdas, warnings & ICEs
On 01/31/2017 10:04 AM, Jason Merrill wrote: Agreed. As I was suggesting in response to one of Adam's patches, I think we need to defer creating the closure until f is instantiated; at that point we can resolve all names from f and so we should be able to always push to top when instantiating the lambda. Yup. Perhaps if (!fn_context || fn_context != current_function_decl) That works fine, thanks. It also makes it clearer that 'nested' must be true if !push_to_top (but not vice-versa), which allowed a little more simplification. Committed the attached to trunk. nathan -- Nathan Sidwell 2017-01-31 Nathan SidwellPR c++/67273 PR c++/79253 * pt.c: (instantiate_decl): Push to top level when current function scope doesn't match. Only push lmabda scope stack when pushing to top. PR c++/67273 PR c++/79253 * g++.dg/cpp1y/pr67273.C: New. * g++.dg/cpp1y/pr79253.C: New. Index: cp/pt.c === --- cp/pt.c (revision 245066) +++ cp/pt.c (working copy) @@ -22666,20 +22666,21 @@ instantiate_decl (tree d, bool defer_ok, goto out; } - bool nested; + bool push_to_top, nested; tree fn_context; fn_context = decl_function_context (d); - nested = (current_function_decl != NULL_TREE); + nested = current_function_decl != NULL_TREE; + push_to_top = !(nested && fn_context == current_function_decl); + vec omp_privatization_save; if (nested) save_omp_privatization_clauses (omp_privatization_save); - if (!fn_context) + if (push_to_top) push_to_top_level (); else { - if (nested) - push_function_context (); + push_function_context (); cp_unevaluated_operand = 0; c_inhibit_evaluation_warnings = 0; } @@ -22756,7 +22757,7 @@ instantiate_decl (tree d, bool defer_ok, block = push_stmt_list (); else { - if (LAMBDA_FUNCTION_P (d)) + if (push_to_top && LAMBDA_FUNCTION_P (d)) { /* When instantiating a lambda's templated function operator, we need to push the non-lambda class scope @@ -22849,9 +22850,9 @@ instantiate_decl (tree d, bool defer_ok, /* We're not deferring instantiation any more. */ TI_PENDING_TEMPLATE_FLAG (DECL_TEMPLATE_INFO (d)) = 0; - if (!fn_context) + if (push_to_top) pop_from_top_level (); - else if (nested) + else pop_function_context (); if (nested) Index: testsuite/g++.dg/cpp1y/pr67273.C === --- testsuite/g++.dg/cpp1y/pr67273.C (revision 0) +++ testsuite/g++.dg/cpp1y/pr67273.C (working copy) @@ -0,0 +1,16 @@ +// { dg-do compile { target c++14 } } +// { dg-additional-options "-Wshadow" } + +// pr67273 bogus warning about shadowing. + + +template void Foo (T &) +{ + int ARG = 2; + lambda (1); +} + +void Baz () +{ + Foo ([] (auto &) {}); +} Index: testsuite/g++.dg/cpp1y/pr79253.C === --- testsuite/g++.dg/cpp1y/pr79253.C (revision 0) +++ testsuite/g++.dg/cpp1y/pr79253.C (working copy) @@ -0,0 +1,33 @@ +// { dg-do compile { target c++14 } } +// PR 79253 ICE instantiating lambda body. + +template struct A; +template > class B {}; +template void foo (U, V) { T (0, 0); } +struct C {}; +template class F, class> +void +bar () +{ + F<0, 0, 0>::baz; +} +struct G { int l; }; +template struct E : C +{ + E (int, int) : e (0) + { +auto = this->m (); +auto c = [&] { m.l; }; + } + G (); + int e; +}; +struct D +{ + void + baz () { bar >; } + template struct F + { +static B<> baz () { foo > (0, 0); } + }; +};
Re: [C++ PATCH] pr 67273 & 79253 lambdas, warnings & ICEs
On Tue, Jan 31, 2017 at 8:31 AM, Nathan Sidwellwrote: > On 01/30/2017 03:48 PM, Jason Merrill wrote: >> Why can't it figure that out for itself? We should be able to tell >> whether its containing function is currently open. > > It doesn't have sufficient information (but that may not matter, see below). > > template void Foo (T lam) > { > lam (1u); // #1 > } > > template > void f(T x) > { > auto lam = [](auto x) { return (x); }; > > lam (1); // #2 > Foo (lam); > } > > void Bar () > { > f(1); > } > > at #1 and #2 we end up via maybe_instantiate in instantiate_decl (to > determine the return type when building the CALL_EXPR). At #1 > current_function_decl is the template Foo, which is not the context of the > closure type. > > At #2 we go via the same path, but current_function_decl is 'f', which is > the context of the closure type. > > It seems wrong to me to push to top in one of those cases but not in the > other. Again, absent of generic lambdas, we'd always push to top in these > circumstances. Agreed. As I was suggesting in response to one of Adam's patches, I think we need to defer creating the closure until f is instantiated; at that point we can resolve all names from f and so we should be able to always push to top when instantiating the lambda. > That said, using the predicate: >current_function_decl > == DECL_CONTEXT (TYPE_NAME (CP_DECL_CONTEXT (d))) > to determine whether a lambda instantiate should push to top or not doesn't > cause any test failures. (and does resolve the shadowing bug). Perhaps if (!fn_context || fn_context != current_function_decl) ? Jason
Re: [C++ PATCH] pr 67273 & 79253 lambdas, warnings & ICEs
On 01/30/2017 03:48 PM, Jason Merrill wrote: Why can't it figure that out for itself? We should be able to tell whether its containing function is currently open. It doesn't have sufficient information (but that may not matter, see below). template void Foo (T lam) { lam (1u); // #1 } template void f(T x) { auto lam = [](auto x) { return (x); }; lam (1); // #2 Foo (lam); } void Bar () { f(1); } at #1 and #2 we end up via maybe_instantiate in instantiate_decl (to determine the return type when building the CALL_EXPR). At #1 current_function_decl is the template Foo, which is not the context of the closure type. At #2 we go via the same path, but current_function_decl is 'f', which is the context of the closure type. It seems wrong to me to push to top in one of those cases but not in the other. Again, absent of generic lambdas, we'd always push to top in these circumstances. That said, using the predicate: current_function_decl == DECL_CONTEXT (TYPE_NAME (CP_DECL_CONTEXT (d))) to determine whether a lambda instantiate should push to top or not doesn't cause any test failures. (and does resolve the shadowing bug). In case you're wondering, if we always push to top for a lambda a bunch of tests fail. It seems (at least) the access checking breaks. nathan -- Nathan Sidwell
Re: [C++ PATCH] pr 67273 & 79253 lambdas, warnings & ICEs
On Fri, Jan 27, 2017 at 7:45 AM, Nathan Sidwellwrote: > Jason, > I happened to be working on 67273, noticed a problem with my 77585 fix, and > coincidentally 79253 got filed, which this also fixes. > > In 67253, Wshadow checking was getting confused when determining the return > type of an instantiated lambda. > > template void Foo (T &) { > int ARG = 2; > lambda (1); > } > > void Baz () { > Foo ([] (auto &) {}); > } > > maybe_instantiate_decl gets called when building the 'lambda (1)' call > during the instantiation of Foo (to determine its return type). It goes > ahead and calls instantiate_decl. instantiate_decl decides not to push to > top level at: > > fn_context = decl_function_context (d); > ... > if (!fn_context) > push_to_top_level (); > else > ... > > because 'decl_function_context' is true (it's 'Baz'), but the current > function is 'Foo'. We end up in store_parms thinking we're > pushing a parm 'ARG' that shadows the local var 'ARG'. That doesn't result > in wrong code, but does give a spurious shadowing warning. > > Again, this is an artifact of generic lambdas being template members of > function-scope classes. Not a thing that exists elsewhere. > > Unfortunately, there is a case where we do want to stay at the current level > -- when we're instantiating the lambda body during instantiation of the > containing template function. instantiate_decl must be told in which > context it's being invoked. Why can't it figure that out for itself? We should be able to tell whether its containing function is currently open. Jason
[C++ PATCH] pr 67273 & 79253 lambdas, warnings & ICEs
Jason, I happened to be working on 67273, noticed a problem with my 77585 fix, and coincidentally 79253 got filed, which this also fixes. In 67253, Wshadow checking was getting confused when determining the return type of an instantiated lambda. template void Foo (T &) { int ARG = 2; lambda (1); } void Baz () { Foo ([] (auto &) {}); } maybe_instantiate_decl gets called when building the 'lambda (1)' call during the instantiation of Foo (to determine its return type). It goes ahead and calls instantiate_decl. instantiate_decl decides not to push to top level at: fn_context = decl_function_context (d); ... if (!fn_context) push_to_top_level (); else ... because 'decl_function_context' is true (it's 'Baz'), but the current function is 'Foo'. We end up in store_parms thinking we're pushing a parm 'ARG' that shadows the local var 'ARG'. That doesn't result in wrong code, but does give a spurious shadowing warning. Again, this is an artifact of generic lambdas being template members of function-scope classes. Not a thing that exists elsewhere. Unfortunately, there is a case where we do want to stay at the current level -- when we're instantiating the lambda body during instantiation of the containing template function. instantiate_decl must be told in which context it's being invoked. the 77585 problem is: if (LAMBDA_FUNCTION_P (d)) { /* When instantiating a lambda's templated function operator, we need to push the non-lambda class scope of the lambda itself so that the nested function stack is sufficiently correct to deal with this capture. */ We don't want to do that when we're not pushing to top level (I had thought it was just extra work, but 79253 shows it's a correctness problem). As 'defer_ok' is still an int parm, I did toy with making it tri-valued, but went for adding an additional parm, and correcting defer_ok's type in the process. All callers outside of pt.c were already passing true or false. ok? nathan -- Nathan Sidwell 2017-01-26 Nathan SidwellPR c++/67273 PR c++/79253 * cp-tree.h (instantiate_decl): Make defer_ok bool, add lambda_subst flag. * pt.c: Fix instantiate_decl calls to pass true/false not 0/1 (instantiate_class_template_1): Inform instantiate_decl of lambda fn substing. (instantiate_decl): Push to top level for lambda fn instanitation, except when lambda_subst. PR c++/67273 PR c++/79253 * g++.dg/cpp1y/pr67273.C: New. * g++.dg/cpp1y/pr79253.C: New. Index: cp/cp-tree.h === --- cp/cp-tree.h (revision 244932) +++ cp/cp-tree.h (working copy) @@ -6182,7 +6182,8 @@ extern void do_decl_instantiation (tree extern void do_type_instantiation (tree, tree, tsubst_flags_t); extern bool always_instantiate_p (tree); extern void maybe_instantiate_noexcept (tree); -extern tree instantiate_decl (tree, int, bool); +extern tree instantiate_decl (tree, bool, bool, + bool = false); extern int comp_template_parms (const_tree, const_tree); extern bool uses_parameter_packs(tree); extern bool template_parameter_pack_p (const_tree); Index: cp/pt.c === --- cp/pt.c (revision 244932) +++ cp/pt.c (working copy) @@ -10676,7 +10676,8 @@ instantiate_class_template_1 (tree type) { /* Set function_depth to avoid garbage collection. */ ++function_depth; - instantiate_decl (decl, false, false); + instantiate_decl (decl, /*defer_ok=*/false, false, +/*lambda_subst=*/true); --function_depth; } @@ -16024,7 +16025,8 @@ tsubst_expr (tree t, tree args, tsubst_f complete_type (tmp); for (fn = TYPE_METHODS (tmp); fn; fn = DECL_CHAIN (fn)) if (!DECL_ARTIFICIAL (fn)) - instantiate_decl (fn, /*defer_ok*/0, /*expl_inst_class*/false); + instantiate_decl (fn, /*defer_ok=*/false, +/*expl_inst_class=*/false); } break; @@ -21941,7 +21943,7 @@ do_decl_instantiation (tree decl, tree s check_explicit_instantiation_namespace (result); mark_decl_instantiated (result, extern_p); if (! extern_p) -instantiate_decl (result, /*defer_ok=*/1, +instantiate_decl (result, /*defer_ok=*/true, /*expl_inst_class_mem_p=*/false); } @@ -21979,7 +21981,7 @@ instantiate_class_member (tree decl, int { mark_decl_instantiated (decl, extern_p); if (! extern_p) -instantiate_decl (decl, /*defer_ok=*/1, +instantiate_decl (decl, /*defer_ok=*/true, /*expl_inst_class_mem_p=*/true); } @@ -22400,15 +22402,17 @@ maybe_instantiate_noexcept (tree fn) } /* Produce the definition of D, a _DECL generated from a template. If - DEFER_OK is nonzero, then we don't have to actually do the + DEFER_OK is true, then we don't have to actually do the