Message ID | 1374224442-9360-2-git-send-email-adam@jessamine.co.uk |
---|---|
State | New |
Headers | show |
On 07/19/2013 05:00 AM, Adam Butcher wrote: > + warning (0, "Conversion of a generic lambda to a function pointer is not > currently implemented."); ... > + "Generic lambdas are only supported in C++1y mode."); We generally don't capitalize diagnostics or end them with a period. And the second message should mention "-std=c++1y or -std=gnu++1y". > + push_deferring_access_checks (dk_deferred); Why do you need this? > +/* Nonzero if the function being declared was made a template due to it's "its" > + error ("parameter declared %<auto%> (unsupported prior to C++1y)"); This should also mention the compiler flags. We should probably introduce a maybe_warn_cpp1y function to go along with ..._cpp0x. > + else // extend current template parameter list Do we still need to do this, now that we're handling all the parameters at the end of the parameter list? Jason
Hi Jason, I've addressed your review comments and provided support for conversion to function pointer for generic lambdas. I've sent the patches as updates to the previous set. I'll wait for any further comments before formalizing them into a cleaner set. The changes now support the examples given in N3690 ยง5.1.2.{5,6} and the test program included at the end of this mail. I think it is feature-complete. On 19.07.2013 17:56, Jason Merrill wrote: > On 07/19/2013 05:00 AM, Adam Butcher wrote: > > > > + push_deferring_access_checks (dk_deferred); > > > Why do you need this? > I don't think I do. I had thought that it would be necessary to handle deferred friendship situations but I don't think that can apply to generic lambdas. I've ditched them. > > +/* Nonzero if the function being declared was made a template due to it's > > > "its" > I've fixed this and a couple of others in my own comments. I've also fixed others I found in the same file (as a separate patch). > > + else // extend current template parameter list > > > Do we still need to do this, now that we're handling all the > parameters at the end of the parameter list? > I think extending the current parameter list is still needed when 'auto' is found in a parameter declaration of an existing function template. E.g. template <typename T> void f(T& t, auto& a) { ... } I think that it is necessary to keep everything consistent as if the template had been declared in the 'conventional' way. The template parameter generated for the generic parameter 'a' effectively makes 'f's template parameter list into '<typename T, typename __Gen0>'. Cheers, Adam Patch summary (4): Preserve type qualifiers for implicit template parameters. Support implicit conversion of a stateless generic lambda to a function pointer. Address review comments. Grammar "it's" to "its". gcc/cp/cp-tree.h | 2 +- gcc/cp/decl.c | 3 ++- gcc/cp/lambda.c | 77 +++++++++++++++++++++++++++++++++++++++++--------------- gcc/cp/parser.c | 6 ++--- gcc/cp/pt.c | 21 ++++++++++------ 5 files changed, 76 insertions(+), 33 deletions(-) Test program: /* Function templates at namespace scope. */ auto f1 (auto& a, auto const& b) { return a += b; } template <typename A> auto f2 (A& a, auto const& b) { return a += b; } template <typename B> auto f3 (auto& a, B const& b) { return a += b; } template <typename A, typename B> auto f4 (A& a, B const& b) { return a += b; } struct S { /* Non-static member function templates. */ auto mf1 (auto& a, auto const& b) { return a += b; } template <typename A> auto mf2 (A& a, auto const& b) { return a += b; } template <typename B> auto mf3 (auto& a, B const& b) { return a += b; } template <typename A, typename B> auto mf4 (A& a, B const& b) { return a += b; } /* Static member function templates. */ static auto smf1 (auto& a, auto const& b) { return a += b; } template <typename A> static auto smf2 (A& a, auto const& b) { return a += b; } template <typename B> static auto smf3 (auto& a, B const& b) { return a += b; } template <typename A, typename B> static auto smf4 (A& a, B const& b) { return a += b; } }; #undef NDEBUG #include <cassert> #define CHECK(A, b, f) do { \ A a1 = 5, a2 = 12; \ auto r1 = f (a1, b); \ auto r2 = f (a2, b); \ assert ((#f, a1 == 5 + b)); \ assert ((#f, a2 == 12 + b)); \ assert ((#f, r1 == a1)); \ assert ((#f, r2 == a2)); \ } while (0) #define INVOKEi(f, A, b, i) do { CHECK (A, b, f ## i); } while (0) #define INVOKE4(f, A, b) do { INVOKEi (f, A, b, 1); \ INVOKEi (f, A, b, 2); \ INVOKEi (f, A, b, 3); \ INVOKEi (f, A, b, 4); } while (0) #define AS_FUNi(f, A, b, i) do { CHECK (A, b, f ## i._FUN); } while (0) #define AS_FUN4(f, A, b) do { AS_FUNi (f, A, b, 1); \ AS_FUNi (f, A, b, 2); \ AS_FUNi (f, A, b, 3); \ AS_FUNi (f, A, b, 4); } while (0) #define AS_PTRi(f, A, B, b, i) do { A (*pfn) (A&, B const&) = f ## i; \ CHECK (A, b, pfn); } while (0) #define AS_PTR4(f, A, B, b) do { AS_PTRi (f, A, B, b, 1); \ AS_PTRi (f, A, B, b, 2); \ AS_PTRi (f, A, B, b, 3); \ AS_PTRi (f, A, B, b, 4); } while (0) int main() { /* Check namespace templates. */ INVOKE4 (f, float, 7); AS_PTR4 (f, float, int, 7); /* Check member templates. */ S s; INVOKE4 (s.mf, float, 7); INVOKE4 (s.smf, float, 7); INVOKE4 (S::smf, float, 7); AS_PTR4 (s.smf, float, int, 7); AS_PTR4 (S::smf, float, int, 7); /* Regression check non-template stateless lambda and its conversion to function pointer. */ auto lf0 = [] (float& a, int const& b) { return a += b; }; INVOKEi (lf, float, 7, 0); AS_FUNi (lf, float, 7, 0); AS_PTRi (lf, float, int, 7, 0); /* Check stateless lambda templates. */ auto lf1 = [] (auto& a, auto const& b) { return a += b; }; auto lf2 = [] <typename A> (A& a, auto const& b) { return a += b; }; auto lf3 = [] <typename B> (auto& a, B const& b) { return a += b; }; auto lf4 = [] <typename A, typename B> (A& a, B const& b) { return a += b; }; INVOKE4 (lf, float, 7); AS_FUN4 (lf, float, 7); AS_PTR4 (lf, float, int, 7); /* Check capturing lambda templates. */ int i; auto lc1 = [i] (auto& a, auto const& b) { return a += b; }; auto lc2 = [i] <typename A> (A& a, auto const& b) { return a += b; }; auto lc3 = [i] <typename B> (auto& a, B const& b) { return a += b; }; auto lc4 = [i] <typename A, typename B> (A& a, B const& b) { return a += b; }; INVOKE4 (lc, float, 7); }
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 1573ced..c166f6e 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -507,8 +507,9 @@ check_member_template (tree tmpl) || (TREE_CODE (decl) == TYPE_DECL && MAYBE_CLASS_TYPE_P (TREE_TYPE (decl)))) { - /* The parser rejects template declarations in local classes. */ - gcc_assert (!current_function_decl); + /* The parser rejects template declarations in local classes + (with the exception of generic lambdas). */ + gcc_assert (!current_function_decl || LAMBDA_FUNCTION_P (decl)); /* The parser rejects any use of virtual in a function template. */ gcc_assert (!(TREE_CODE (decl) == FUNCTION_DECL && DECL_VIRTUAL_P (decl))); diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index a53e692..98a7925 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -196,7 +196,7 @@ lambda_function (tree lambda) /*protect=*/0, /*want_type=*/false, tf_warning_or_error); if (lambda) - lambda = BASELINK_FUNCTIONS (lambda); + lambda = STRIP_TEMPLATE (get_first_fn (lambda)); return lambda; } @@ -759,6 +759,13 @@ maybe_add_lambda_conv_op (tree type) if (processing_template_decl) return; + if (DECL_TEMPLATE_INFO (callop) && DECL_TEMPLATE_RESULT + (DECL_TI_TEMPLATE (callop)) == callop) + { + warning (0, "Conversion of a generic lambda to a function pointer is not currently implemented."); + return; + } + if (DECL_INITIAL (callop) == NULL_TREE) { /* If the op() wasn't instantiated due to errors, give up. */ diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 4b683bf..48c95e6 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -8790,6 +8790,7 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr) /* Parse the (optional) middle of a lambda expression. lambda-declarator: + < template-parameter-list [opt] > ( parameter-declaration-clause [opt] ) attribute-specifier [opt] mutable [opt] @@ -8809,10 +8810,32 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) tree param_list = void_list_node; tree attributes = NULL_TREE; tree exception_spec = NULL_TREE; + tree template_param_list = NULL_TREE; tree t; - /* The lambda-declarator is optional, but must begin with an opening - parenthesis if present. */ + /* The template-parameter-list is optional, but must begin with + an opening angle if present. */ + if (cp_lexer_next_token_is (parser->lexer, CPP_LESS)) + { + cp_lexer_consume_token (parser->lexer); + + if (cxx_dialect < cxx1y) + cp_parser_error (parser, + "Generic lambdas are only supported in C++1y mode."); + + push_deferring_access_checks (dk_deferred); + + template_param_list = cp_parser_template_parameter_list (parser); + + cp_parser_skip_to_end_of_template_parameter_list (parser); + + /* We just processed one more parameter list. */ + ++parser->num_template_parameter_lists; + } + + /* The parameter-declaration-clause is optional (unless + template-parameter-list was given), but must begin with an + opening parenthesis if present. */ if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) { cp_lexer_consume_token (parser->lexer); @@ -8858,6 +8881,8 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) leave_scope (); } + else if (template_param_list != NULL_TREE) // generate diagnostic + cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN); /* Create the function call operator. @@ -8901,6 +8926,13 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) DECL_ARTIFICIAL (fco) = 1; /* Give the object parameter a different name. */ DECL_NAME (DECL_ARGUMENTS (fco)) = get_identifier ("__closure"); + if (template_param_list) + { + pop_deferring_access_checks (); + fco = finish_member_template_decl (fco); + finish_template_decl (template_param_list); + --parser->num_template_parameter_lists; + } } finish_member_declaration (fco); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index de054ac..3694ccc 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -9028,7 +9028,9 @@ instantiate_class_template_1 (tree type) tree decl = lambda_function (type); if (decl) { - instantiate_decl (decl, false, false); + if (!DECL_TEMPLATE_INFO (decl) || DECL_TEMPLATE_RESULT + (DECL_TI_TEMPLATE (decl)) != decl) + instantiate_decl (decl, false, false); /* We need to instantiate the capture list from the template after we've instantiated the closure members, but before we