From 281e6c1d8f1b4ca552d8ce2276ddecfcd6ffb15e Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Thu, 28 Sep 2017 15:39:45 -0400 Subject: [PATCH] PR c++/56973, DR 696 - capture constant variables only as needed. * expr.c (mark_use): Split out from mark_rvalue_use and mark_lvalue_use. Handle lambda capture of constant variables. (mark_lvalue_use_nonread): New. * semantics.c (process_outer_var_ref): Don't capture a constant variable until forced. * pt.c (processing_nonlambda_template): New. * call.c (build_this): Check it. * decl2.c (grok_array_decl): Call mark_rvalue_use and mark_lvalue_use_nonread. * init.c (constant_value_1): Don't call mark_rvalue_use. * typeck.c (build_static_cast): Handle lambda capture. From-SVN: r253266 --- gcc/cp/ChangeLog | 13 +++ gcc/cp/call.c | 2 +- gcc/cp/cp-tree.h | 4 +- gcc/cp/decl2.c | 5 + gcc/cp/expr.c | 107 ++++++++++++++++-- gcc/cp/init.c | 1 - gcc/cp/pt.c | 26 +++++ gcc/cp/semantics.c | 24 ++-- gcc/cp/typeck.c | 14 ++- gcc/testsuite/g++.dg/cpp0x/constexpr-64462.C | 2 +- .../g++.dg/cpp0x/lambda/lambda-const6.C | 15 +++ .../g++.dg/cpp0x/lambda/lambda-const7.C | 12 ++ .../g++.dg/cpp1y/lambda-generic-const4.C | 2 +- 13 files changed, 196 insertions(+), 31 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const6.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const7.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 2936f22a47e..6e2e3a8539e 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,18 @@ 2017-09-28 Jason Merrill + PR c++/56973, DR 696 - capture constant variables only as needed. + * expr.c (mark_use): Split out from mark_rvalue_use and + mark_lvalue_use. Handle lambda capture of constant variables. + (mark_lvalue_use_nonread): New. + * semantics.c (process_outer_var_ref): Don't capture a constant + variable until forced. + * pt.c (processing_nonlambda_template): New. + * call.c (build_this): Check it. + * decl2.c (grok_array_decl): Call mark_rvalue_use and + mark_lvalue_use_nonread. + * init.c (constant_value_1): Don't call mark_rvalue_use. + * typeck.c (build_static_cast): Handle lambda capture. + Use local_specializations to find capture proxies. * cp-tree.h (DECL_CAPTURED_VARIABLE): New. * lambda.c (build_capture_proxy): Set it. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 99a7b77efb2..05dc8bbdab7 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -3362,7 +3362,7 @@ build_this (tree obj) { /* In a template, we are only concerned about the type of the expression, so we can take a shortcut. */ - if (processing_template_decl) + if (processing_nonlambda_template ()) return build_address (obj); return cp_build_addr_expr (obj, tf_warning_or_error); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index a6349019543..f56c9517967 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6249,6 +6249,7 @@ extern tree mark_rvalue_use (tree, location_t = UNKNOWN_LOCATION, bool = true); extern tree mark_lvalue_use (tree); +extern tree mark_lvalue_use_nonread (tree); extern tree mark_type_use (tree); extern void mark_exp_read (tree); @@ -6412,6 +6413,7 @@ extern tree lookup_template_variable (tree, tree); extern int uses_template_parms (tree); extern bool uses_template_parms_level (tree, int); extern bool in_template_function (void); +extern bool processing_nonlambda_template (void); extern tree instantiate_class_template (tree); extern tree instantiate_template (tree, tree, tsubst_flags_t); extern tree fn_type_unification (tree, tree, tree, @@ -6720,7 +6722,7 @@ extern tree finish_template_type (tree, tree, int); extern tree finish_base_specifier (tree, tree, bool); extern void finish_member_declaration (tree); extern bool outer_automatic_var_p (tree); -extern tree process_outer_var_ref (tree, tsubst_flags_t); +extern tree process_outer_var_ref (tree, tsubst_flags_t, bool force_use = false); extern cp_expr finish_id_expression (tree, tree, tree, cp_id_kind *, bool, bool, bool *, diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 03e91b7e150..29d6c59f549 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -427,6 +427,11 @@ grok_array_decl (location_t loc, tree array_expr, tree index_exp, if (array_expr == error_mark_node || index_exp == error_mark_node) error ("ambiguous conversion for array subscript"); + if (TREE_CODE (TREE_TYPE (array_expr)) == POINTER_TYPE) + array_expr = mark_rvalue_use (array_expr); + else + array_expr = mark_lvalue_use_nonread (array_expr); + index_exp = mark_rvalue_use (index_exp); expr = build_array_ref (input_location, array_expr, index_exp); } if (processing_template_decl && expr != error_mark_node) diff --git a/gcc/cp/expr.c b/gcc/cp/expr.c index 8bd341b814e..f5c8e801918 100644 --- a/gcc/cp/expr.c +++ b/gcc/cp/expr.c @@ -86,21 +86,105 @@ cplus_expand_constant (tree cst) return cst; } +/* We've seen an actual use of EXPR. Possibly replace an outer variable + reference inside with its constant value or a lambda capture. */ + +static tree +mark_use (tree expr, bool rvalue_p, bool read_p, + location_t loc /* = UNKNOWN_LOCATION */, + bool reject_builtin /* = true */) +{ +#define RECUR(t) mark_use ((t), rvalue_p, read_p, loc, reject_builtin) + + if (reject_builtin && reject_gcc_builtin (expr, loc)) + return error_mark_node; + + if (read_p) + mark_exp_read (expr); + + bool recurse_op[3] = { false, false, false }; + switch (TREE_CODE (expr)) + { + case VAR_DECL: + if (outer_automatic_var_p (expr) + && decl_constant_var_p (expr)) + { + if (rvalue_p) + { + tree t = maybe_constant_value (expr); + if (TREE_CONSTANT (t)) + { + expr = t; + break; + } + } + expr = process_outer_var_ref (expr, tf_warning_or_error, true); + expr = convert_from_reference (expr); + } + break; + case COMPONENT_REF: + recurse_op[0] = true; + break; + case COMPOUND_EXPR: + recurse_op[1] = true; + break; + case COND_EXPR: + recurse_op[2] = true; + if (TREE_OPERAND (expr, 1)) + recurse_op[1] = true; + break; + case INDIRECT_REF: + if (REFERENCE_REF_P (expr)) + { + /* Try to look through the reference. */ + tree ref = TREE_OPERAND (expr, 0); + tree r = mark_rvalue_use (ref, loc, reject_builtin); + if (r != ref) + { + expr = copy_node (expr); + TREE_OPERAND (expr, 0) = r; + } + } + break; + default: + break; + } + + bool changed = false; + tree ops[3]; + for (int i = 0; i < 3; ++i) + if (recurse_op[i]) + { + tree op = TREE_OPERAND (expr, i); + ops[i] = RECUR (op); + if (ops[i] != op) + changed = true; + } + + if (changed) + { + expr = copy_node (expr); + for (int i = 0; i < 3; ++i) + if (recurse_op[i]) + TREE_OPERAND (expr, i) = ops[i]; + } + + return expr; +#undef RECUR +} + /* Called whenever the expression EXPR is used in an rvalue context. When REJECT_BUILTIN is true the expression is checked to make sure it doesn't make it possible to obtain the address of a GCC built-in function with no library fallback (or any of its bits, such as in a conversion to bool). */ + tree -mark_rvalue_use (tree expr, +mark_rvalue_use (tree e, location_t loc /* = UNKNOWN_LOCATION */, bool reject_builtin /* = true */) { - if (reject_builtin && reject_gcc_builtin (expr, loc)) - return error_mark_node; - - mark_exp_read (expr); - return expr; + return mark_use (e, true, true, loc, reject_builtin); } /* Called whenever an expression is used in an lvalue context. */ @@ -108,8 +192,15 @@ mark_rvalue_use (tree expr, tree mark_lvalue_use (tree expr) { - mark_exp_read (expr); - return expr; + return mark_use (expr, false, true, input_location, false); +} + +/* As above, but don't consider this use a read. */ + +tree +mark_lvalue_use_nonread (tree expr) +{ + return mark_use (expr, false, false, input_location, false); } /* Called whenever an expression is used in a type use context. */ diff --git a/gcc/cp/init.c b/gcc/cp/init.c index b01d662fef2..4bc0755cdcb 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -2213,7 +2213,6 @@ constant_value_1 (tree decl, bool strict_p, bool return_aggregate_cst_ok_p) initializer for the static data member is not processed until needed; we need it now. */ mark_used (decl, tf_none); - mark_rvalue_use (decl); init = DECL_INITIAL (decl); if (init == error_mark_node) { diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 2bdac6de6c4..0dae10e032b 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -9494,6 +9494,32 @@ in_template_function (void) return ret; } +/* Returns true iff we are currently within a template other than a generic + lambda. We test this by finding the outermost closure type and checking + whether it is dependent. */ + +bool +processing_nonlambda_template (void) +{ + if (!processing_template_decl) + return false; + + tree outer_closure = NULL_TREE; + for (tree t = current_class_type; t; + t = decl_type_context (TYPE_MAIN_DECL (t))) + { + if (LAMBDA_TYPE_P (t)) + outer_closure = t; + else + break; + } + + if (outer_closure) + return dependent_type_p (outer_closure); + else + return true; +} + /* Returns true if T depends on any template parameter with level LEVEL. */ bool diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 4e87e47d9b3..d96423f2348 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -3282,7 +3282,7 @@ outer_automatic_var_p (tree decl) rewrite it for lambda capture. */ tree -process_outer_var_ref (tree decl, tsubst_flags_t complain) +process_outer_var_ref (tree decl, tsubst_flags_t complain, bool force_use) { if (cp_unevaluated_operand) /* It's not a use (3.2) if we're in an unevaluated context. */ @@ -3303,6 +3303,12 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain) if (parsing_nsdmi ()) containing_function = NULL_TREE; + /* Core issue 696: Only an odr-use of an outer automatic variable causes a + capture (or error), and a constant variable can decay to a prvalue + constant without odr-use. So don't capture yet. */ + if (decl_constant_var_p (decl) && !force_use) + return decl; + if (containing_function && LAMBDA_FUNCTION_P (containing_function)) { /* Check whether we've already built a proxy. */ @@ -3314,7 +3320,7 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain) return d; else /* We need to capture an outer proxy. */ - return process_outer_var_ref (d, complain); + return process_outer_var_ref (d, complain, force_use); } } @@ -3353,20 +3359,6 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain) && uses_template_parms (DECL_TI_ARGS (containing_function))) return decl; - /* Core issue 696: "[At the July 2009 meeting] the CWG expressed - support for an approach in which a reference to a local - [constant] automatic variable in a nested class or lambda body - would enter the expression as an rvalue, which would reduce - the complexity of the problem" - - FIXME update for final resolution of core issue 696. */ - if (decl_constant_var_p (decl)) - { - tree t = maybe_constant_value (convert_from_reference (decl)); - if (TREE_CONSTANT (t)) - return t; - } - if (lambda_expr && VAR_P (decl) && DECL_ANON_UNION_VAR_P (decl)) { diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 028d56ff18c..326721eb5e0 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -7044,16 +7044,24 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p, /* Return an expression representing static_cast(EXPR). */ tree -build_static_cast (tree type, tree expr, tsubst_flags_t complain) +build_static_cast (tree type, tree oexpr, tsubst_flags_t complain) { + tree expr = oexpr; tree result; bool valid_p; if (type == error_mark_node || expr == error_mark_node) return error_mark_node; - if (processing_template_decl) + bool dependent = (dependent_type_p (type) + || type_dependent_expression_p (expr)); + if (dependent) { + tmpl: + expr = oexpr; + if (dependent) + /* Handle generic lambda capture. */ + expr = mark_lvalue_use (expr); expr = build_min (STATIC_CAST_EXPR, type, expr); /* We don't know if it will or will not have side effects. */ TREE_SIDE_EFFECTS (expr) = 1; @@ -7076,6 +7084,8 @@ build_static_cast (tree type, tree expr, tsubst_flags_t complain) maybe_warn_about_useless_cast (type, expr, complain); maybe_warn_about_cast_ignoring_quals (type, complain); } + if (processing_template_decl) + goto tmpl; return result; } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-64462.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-64462.C index 69193fd0db7..8200f871057 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-64462.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-64462.C @@ -6,5 +6,5 @@ int z; int main() { constexpr int& y = x; - [=] { z = y; }(); + [] { z = y; }(); } diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const6.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const6.C new file mode 100644 index 00000000000..4edfb70038f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const6.C @@ -0,0 +1,15 @@ +// PR c++/56973 +// { dg-do compile { target c++11 } } + +int f() +{ + const int i = 42; + auto j = *[=]{ return &i; }(); + auto k = []{ return i; }(); + return j+k; +} + +int main() +{ + return f() != 84; +} diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const7.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const7.C new file mode 100644 index 00000000000..64a37b80a9b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const7.C @@ -0,0 +1,12 @@ +// { dg-do compile { target c++11 } } +// { dg-options -w } + +int main() +{ + const int i = 4; + [] { constexpr int x = i; }; + [=] { &i; constexpr int x = i; }; + [&] { &i; constexpr int x = i; }; + [i] { &i; constexpr int x = i; }; + [&i] { &i; constexpr int x = i; }; +} diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4.C index 52f4373ccbd..d56f379c680 100644 --- a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4.C +++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4.C @@ -13,7 +13,7 @@ template void bar (T) { constexpr auto N = a<1>; auto f = [&] (auto i) { - static_assert (static_cast(N) == 1, ""); + return static_cast(N) == 1; }; foo (f); } -- 2.30.2