Message ID | 1375861956-11593-1-git-send-email-adam@jessamine.co.uk |
---|---|
State | New |
Headers | show |
On 08/07/2013 03:52 AM, Adam Butcher wrote: > But a cleaner way might be to extend the "processing > template declaration" state from lambda declarator all the way to the end of the > lambda body. This would match with the scenario that occurs with a standard > in-class member function template definition. To do that elegantly would > require a bit of refactoring of the lambda parser code. It isn't already set through the whole lambda body? That seems necessary to support non-trivial lambda bodies; otherwise we won't be able to handle dependence properly. > I'm not sure which dialect guards to put these features behind though. > Strictly: > > a) Generic lambdas created fully implicitly via 'auto params' should be > accepted with -std=c++1y and -std=gnu++1y since it is actually spec'd by > the draft. > > b) Generic lambdas created with an explicit template parameter list should be > accepted with -std=gnu++1y only. > > c) Generalized implicit function templates should be accepted by -std=gnu++1y > only. This makes sense to me. Or perhaps add (c) to the concepts lite flag, when there is one. Jason
On 07.08.2013 16:59, Jason Merrill wrote: > On 08/07/2013 03:52 AM, Adam Butcher wrote: >> But a cleaner way might be to extend the "processing >> template declaration" state from lambda declarator all the way to >> the end of the >> lambda body. This would match with the scenario that occurs with a >> standard >> in-class member function template definition. To do that elegantly >> would >> require a bit of refactoring of the lambda parser code. > > It isn't already set through the whole lambda body? > No. It is within cp_parser_lambda_declarator_opt. > That seems > necessary to support non-trivial lambda bodies; otherwise we won't be > able to handle dependence properly. > Agreed. Okay, I will produce three patches: 1) Refactor existing monomorphic implementation to give a cleaner parser separation of concerns; extract the fco creation and provide begin and end for the lambda function operator. 2) Add explicit lambda template support. 3) Add implicit function template support. >> I'm not sure which dialect guards to put these features behind >> though. >> Strictly: >> >> a) Generic lambdas created fully implicitly via 'auto params' >> should be >> accepted with -std=c++1y and -std=gnu++1y since it is actually >> spec'd by >> the draft. >> >> b) Generic lambdas created with an explicit template parameter >> list should be >> accepted with -std=gnu++1y only. >> >> c) Generalized implicit function templates should be accepted by >> -std=gnu++1y >> only. > > This makes sense to me. Or perhaps add (c) to the concepts lite > flag, when there is one. > Okay. Will do as originally suggested. I am intending to try out constrained implicit function templates on the c++-concepts branch at some point also (which might already have such a flag). Cheers, Adam
On 07.08.2013 20:56, Adam Butcher wrote: > On 07.08.2013 16:59, Jason Merrill wrote: >> On 08/07/2013 03:52 AM, Adam Butcher wrote: >>> But a cleaner way might be to extend the "processing >>> template declaration" state from lambda declarator all the way to >>> the end of the >>> lambda body. This would match with the scenario that occurs with a >>> standard >>> in-class member function template definition. To do that elegantly >>> would >>> require a bit of refactoring of the lambda parser code. >> >> It isn't already set through the whole lambda body? >> > No. It is within cp_parser_lambda_declarator_opt. > >> That seems >> necessary to support non-trivial lambda bodies; otherwise we won't >> be >> able to handle dependence properly. >> > Agreed. Okay, I will produce three patches: > > 1) Refactor existing monomorphic implementation to give a cleaner > parser separation of concerns; extract the fco creation and > provide begin and end for the lambda function operator. > I did this---well not exactly as written---but I extended the template decl state through until the end of the lambda body. And it works with my dependence test. However, the previous version does too. The following program works fine (conv ops an all) with the refactored version and without it. I'm now questioning whether it is worth making any change in this area. What do you think? The test code is as follows and the resulting program correctly executes S::N::test() 4 times as expected -- were you thinking of some other dependence case? #include <iostream> struct S { struct N { float test () { std::cout << "S::N::test() -> 7.f\n"; return 7.f; } }; }; int main() { auto f = [] <typename T> (T const& s) { typename T::N x; return x.test (); }; auto g = [] (auto const& s) { typename std::decay<decltype (s)>::type::N x; return x.test (); }; S i; f(i); g(i); float (*pfn) (S const&) = f; pfn(i); pfn = g; return pfn(i); } Error messages are reasonable also. Omitting the necessary 'typename's gives: : need ‘typename’ before ‘T::N’ because ‘T’ is a dependent scope : need ‘typename’ before ‘typename std::decay<decltype (s)>::type::N’ because ‘typename std::decay<decltype (s)>::type’ is a dependent scope Passing a local 'struct X {}' instead of 'S' gives: : In instantiation of ‘auto main()::<lambda(const T&)> const [with T = main()::X]’: :24:9: required from here :14:23: error: no type named ‘N’ in ‘struct main()::X’ ... So all seems to be okay with both versions. Any ideas why? Cheers, Adam
On 08/08/2013 06:28 PM, Adam Butcher wrote:
> So all seems to be okay with both versions. Any ideas why?
Hmm, it sounds like processing_template_decl is being set after all,
even without your change.
Jason
On 09.08.2013 03:01, Jason Merrill wrote: > On 08/08/2013 06:28 PM, Adam Butcher wrote: >> So all seems to be okay with both versions. Any ideas why? > > Hmm, it sounds like processing_template_decl is being set after all, > even without your change. > Yup. Although the lambda template code I originally added scoped it to within the the 'cp_parser_lambda_declarator_opt' function, since the call op being declared is an in-class inline member, 'start_preparsed_function' is entering 'maybe_begin_member_template_processing'. 'finish_function' matches that with 'maybe_end_member_template_processing' prior to the function being passed to 'expand_or_defer_fn' (which then causes the erroneous symtab entry). So I think my original fix might be okay here; omitting 'expand_or_defer_fn' if 'generic_lambda_p'. What do you think? Shall I just include it as part of the first patch? If so that'd just be two patches, "teach GCC to deal with lambda templates" and "implicit function templates with auto". Cheers, Adam
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index c90a27c..5fdc551 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -915,7 +915,9 @@ maybe_add_lambda_conv_op (tree type) finish_compound_stmt (compound_stmt); finish_function_body (body); - expand_or_defer_fn (finish_function (2)); + fn = finish_function (/*inline*/2); + if (!generic_lambda_p) + expand_or_defer_fn (fn); /* Generate the body of the conversion op. */ @@ -931,7 +933,9 @@ maybe_add_lambda_conv_op (tree type) finish_compound_stmt (compound_stmt); finish_function_body (body); - expand_or_defer_fn (finish_function (2)); + fn = finish_function (/*inline*/2); + if (!generic_lambda_p) + expand_or_defer_fn (fn); if (nested) pop_function_context (); diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 7cd197d..8107ff1 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -9135,7 +9135,11 @@ cp_parser_lambda_body (cp_parser* parser, tree lambda_expr) finish_lambda_scope (); /* Finish the function and generate code for it if necessary. */ - expand_or_defer_fn (finish_function (/*inline*/2)); + tree fn = finish_function (/*inline*/2); + + /* Only expand if the call op is not a template. */ + if (!DECL_TEMPLATE_INFO (fco)) + expand_or_defer_fn (fn); } parser->local_variables_forbidden_p = local_variables_forbidden_p; diff --git a/gcc/symtab.c b/gcc/symtab.c index a23a905..d15881b 100644 --- a/gcc/symtab.c +++ b/gcc/symtab.c @@ -116,15 +116,6 @@ insert_to_assembler_name_hash (symtab_node node, bool with_clones) tree name = DECL_ASSEMBLER_NAME (node->symbol.decl); - - // FIXME: how does this nullptr get here when declaring a C++ - // FIXME: generic lambda and including iostream (or presumably - // FIXME: any other header with whatever property is triggering - // FIXME: this)!? - // - if (name == 0) - return; - aslot = htab_find_slot_with_hash (assembler_name_hash, name, decl_assembler_name_hash (name), INSERT); @@ -165,15 +156,6 @@ unlink_from_assembler_name_hash (symtab_node node, bool with_clones) else { tree name = DECL_ASSEMBLER_NAME (node->symbol.decl); - - // FIXME: how does this nullptr get here when declaring a C++ - // FIXME: generic lambda and including iostream (or presumably - // FIXME: any other header with whatever property is triggering - // FIXME: this)!? - // - if (name == 0) - return; - void **slot; slot = htab_find_slot_with_hash (assembler_name_hash, name, decl_assembler_name_hash (name),