From 8ca333471792082b70360b87c33a1219534b4ed1 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Tue, 27 Sep 2016 21:10:38 +0200 Subject: [PATCH] Implement P0018R3, C++17 lambda capture of *this by value as [=,*this] Implement P0018R3, C++17 lambda capture of *this by value as [=,*this] * parser.c (cp_parser_lambda_introducer): Formatting fix. Pass true instead of false as by_reference_p to add_capture for 'this'. Parse '*this' simple-capture. * lambda.c (build_capture_proxy): Handle '*this' capture by value. (add_capture): Adjust function comment. For id == this_identifier, treat by_reference_p as capturing '*this' by reference, i.e. 'this' by value, and !by_reference_p as capturing '*this' by value. (add_default_capture): For implicit 'this' capture, always pass by_reference_p true rather than false. * g++.dg/cpp1z/lambda-this1.C: New test. * g++.dg/cpp1z/lambda-this2.C: New test. From-SVN: r240556 --- gcc/cp/ChangeLog | 11 +++++ gcc/cp/lambda.c | 23 +++++++++-- gcc/cp/parser.c | 20 ++++++++- gcc/testsuite/ChangeLog | 3 ++ gcc/testsuite/g++.dg/cpp1z/lambda-this1.C | 68 +++++++++++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp1z/lambda-this2.C | 68 +++++++++++++++++++++++++++++++ 6 files changed, 188 insertions(+), 5 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1z/lambda-this1.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/lambda-this2.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 75c9d36..1a1f186 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,16 @@ 2016-09-27 Jakub Jelinek + Implement P0018R3, C++17 lambda capture of *this by value as [=,*this] + * parser.c (cp_parser_lambda_introducer): Formatting fix. Pass + true instead of false as by_reference_p to add_capture for 'this'. + Parse '*this' simple-capture. + * lambda.c (build_capture_proxy): Handle '*this' capture by value. + (add_capture): Adjust function comment. For id == this_identifier, + treat by_reference_p as capturing '*this' by reference, i.e. 'this' + by value, and !by_reference_p as capturing '*this' by value. + (add_default_capture): For implicit 'this' capture, always pass + by_reference_p true rather than false. + PR c++/77722 * cp-gimplify.c (cp_ubsan_maybe_instrument_return): Instrument also functions that have just a STATEMENT_LIST instead of BIND_EXPR, or diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index d511185..cd32226 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -380,6 +380,13 @@ build_capture_proxy (tree member) type = lambda_proxy_type (object); + if (name == this_identifier && !POINTER_TYPE_P (type)) + { + type = build_pointer_type (type); + type = cp_build_qualified_type (type, TYPE_QUAL_CONST); + object = build_fold_addr_expr_with_type (object, type); + } + if (DECL_VLA_CAPTURE_P (member)) { /* Rebuild the VLA type from the pointer and maxindex. */ @@ -440,7 +447,8 @@ vla_capture_type (tree array_type) /* From an ID and INITIALIZER, create a capture (by reference if BY_REFERENCE_P is true), add it to the capture-list for LAMBDA, - and return it. */ + and return it. If ID is `this', BY_REFERENCE_P says whether + `*this' is captured by reference. */ tree add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p, @@ -499,7 +507,14 @@ add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p, type = lambda_capture_field_type (initializer, explicit_init_p); if (type == error_mark_node) return error_mark_node; - if (by_reference_p) + if (id == this_identifier && !by_reference_p) + { + gcc_assert (POINTER_TYPE_P (type)); + type = TREE_TYPE (type); + initializer = cp_build_indirect_ref (initializer, RO_NULL, + tf_warning_or_error); + } + if (id != this_identifier && by_reference_p) { type = build_reference_type (type); if (!dependent_type_p (type) && !lvalue_p (initializer)) @@ -628,8 +643,8 @@ add_default_capture (tree lambda_stack, tree id, tree initializer) id, initializer, /*by_reference_p=*/ - (!this_capture_p - && (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda) + (this_capture_p + || (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda) == CPLD_REFERENCE)), /*explicit_init_p=*/false); initializer = convert_from_reference (var); diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 5ec8b1b..f672b8d 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -9899,7 +9899,25 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr) cp_lexer_consume_token (parser->lexer); add_capture (lambda_expr, /*id=*/this_identifier, - /*initializer=*/finish_this_expr(), + /*initializer=*/finish_this_expr (), + /*by_reference_p=*/true, + explicit_init_p); + continue; + } + + /* Possibly capture `*this'. */ + if (cp_lexer_next_token_is (parser->lexer, CPP_MULT) + && cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_THIS)) + { + location_t loc = cp_lexer_peek_token (parser->lexer)->location; + if (cxx_dialect < cxx1z) + pedwarn (loc, 0, "%<*this%> capture only available with " + "-std=c++1z or -std=gnu++1z"); + cp_lexer_consume_token (parser->lexer); + cp_lexer_consume_token (parser->lexer); + add_capture (lambda_expr, + /*id=*/this_identifier, + /*initializer=*/finish_this_expr (), /*by_reference_p=*/false, explicit_init_p); continue; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 443a917..59e20b7 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,8 @@ 2016-09-27 Jakub Jelinek + * g++.dg/cpp1z/lambda-this1.C: New test. + * g++.dg/cpp1z/lambda-this2.C: New test. + PR c++/77722 * g++.dg/ubsan/return-4.C: New test. * g++.dg/ubsan/return-5.C: New test. diff --git a/gcc/testsuite/g++.dg/cpp1z/lambda-this1.C b/gcc/testsuite/g++.dg/cpp1z/lambda-this1.C new file mode 100644 index 0000000..5a4c5f9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/lambda-this1.C @@ -0,0 +1,68 @@ +// P0018R3 - C++17 lambda capture of *this +// { dg-do compile { target c++11 } } + +struct A { + int a; + void foo () { + int v = 4; + auto b = [*this, this] {}; // { dg-error "already captured 'this'" } + // { dg-error "'*this' capture only available with" "" { target c++14_down } .-1 } + auto c = [this, *this] {}; // { dg-error "already captured 'this'" } + // { dg-error "'*this' capture only available with" "" { target c++14_down } .-1 } + auto d = [this] { return a; }; + auto e = [*this] { return a; }; // { dg-error "'*this' capture only available with" "" { target c++14_down } } + auto f = [this] { a++; }; + auto g = [*this] { a++; }; // { dg-error "in read-only object" } + // { dg-error "'*this' capture only available with" "" { target c++14_down } .-1 } + auto h = [*this] () mutable { a++; };// { dg-error "'*this' capture only available with" "" { target c++14_down } } + auto i = [=] { return a; }; + auto j = [&] { return a; }; + auto k = [=, this] { return a; };// { dg-error "explicit by-copy capture of 'this' redundant with by-copy capture default" } + auto l = [&, this] { return a; }; + auto m = [=, *this] { return a; };// { dg-error "'*this' capture only available with" "" { target c++14_down } } + auto n = [&, *this] { return a; };// { dg-error "'*this' capture only available with" "" { target c++14_down } } + auto o = [*this, &v] { return a + v; };// { dg-error "'*this' capture only available with" "" { target c++14_down } } + auto p = [*this] { this = 0; }; // { dg-error "lvalue required as left operand of assignment" } + // { dg-error "'*this' capture only available with" "" { target c++14_down } .-1 } + } +}; +struct B { + double b; + B () : b (.007) {} + double foo () { + return [this]{ return [*this] { return b; }; }()(); // { dg-error "'*this' capture only available with" "" { target c++14_down } } + } + double bar () { + auto c = []{ return [*this] { return b; }; }; // { dg-error "'this' was not captured for this lambda function" } + } // { dg-error "invalid use of non-static data member 'B::b'" "" { target *-*-* } .-1 } +}; // { dg-error "'*this' capture only available with" "" { target c++14_down } .-2 } +struct C { + int c; + C (const C &) = delete; + void bar () const; + void foo () { + auto d = [this] { return c; }; + auto e = [*this] { return c; }; // { dg-error "use of deleted function" } + // { dg-error "'*this' capture only available with" "" { target c++14_down } .-1 } + auto f = [=] { return c; }; + auto g = [&] { return c; }; + auto h = [this] { bar (); }; + auto i = [*this] { bar (); }; // { dg-error "use of deleted function" } + // { dg-error "'*this' capture only available with" "" { target c++14_down } .-1 } + } +}; +struct D { + int d; + ~D () = delete; + void bar () const; + void foo () { + auto e = [this] { return d; }; + auto f = [*this] { return d; }; // { dg-error "use of deleted function" } + // { dg-error "'*this' capture only available with" "" { target c++14_down } .-1 } + auto g = [=] { return d; }; + auto h = [&] { return d; }; + auto i = [this] { bar (); }; + auto j = [*this] { bar (); }; // { dg-error "use of deleted function" } + // { dg-error "'*this' capture only available with" "" { target c++14_down } .-1 } + } +}; diff --git a/gcc/testsuite/g++.dg/cpp1z/lambda-this2.C b/gcc/testsuite/g++.dg/cpp1z/lambda-this2.C new file mode 100644 index 0000000..5a0066d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/lambda-this2.C @@ -0,0 +1,68 @@ +// P0018R3 - C++17 lambda capture of *this +// { dg-do run { target c++11 } } +// { dg-options "" } + +extern "C" void abort (); + +struct A { + int a, z; + A () : a (4), z (0) {} + A (const A &x) : a (x.a), z (1) {} + void foo () { + if (z != 0) abort (); + auto b = [this] { return &a; }; + auto c = [*this] { return &a; }; // { dg-warning "'*this' capture only available with" "" { target c++14_down } } + auto d = [=] { return &a; }; + auto e = [&] { return &a; }; + if (b () != &a) abort (); + if (*b () != 4) abort (); + auto f = c (); + if (c () == &a) abort (); + if (c () != f) abort (); + if (*c () != 4) abort (); + if (d () != &a) abort (); + if (e () != &a) abort (); + auto g = [this] { return a + z; }; + auto h = [*this] { return a + z; }; // { dg-warning "'*this' capture only available with" "" { target c++14_down } } + auto i = [=] { return a + z; }; + auto j = [&] { return a + z; }; + if (g () != 4 || h () != 5 || i () != 4 || j () != 4) abort (); + } +}; + +template +struct B { + int a, z; + B () : a (N), z (0) {} + B (const B &x) : a (x.a), z (1) {} + void foo () { + if (z != 0) abort (); + auto b = [this] { return &a; }; + auto c = [*this] { return &a; }; // { dg-warning "'*this' capture only available with" "" { target c++14_down } } + auto d = [=] { return &a; }; + auto e = [&] { return &a; }; + if (b () != &a) abort (); + if (*b () != 9) abort (); + auto f = c (); + if (c () == &a) abort (); + if (c () != f) abort (); + if (*c () != 9) abort (); + if (d () != &a) abort (); + if (e () != &a) abort (); + auto g = [this] { return a + z; }; + auto h = [*this] { return a + z; }; // { dg-warning "'*this' capture only available with" "" { target c++14_down } } + auto i = [=] { return a + z; }; + auto j = [&] { return a + z; }; + if (g () != 9 || h () != 10 || i () != 9 || j () != 9) abort (); + } +}; + +int +main () +{ + A a; + a.foo (); + B<9> b; + b.foo (); + return 0; +} -- 2.7.4