From 281e6c1d8f1b4ca552d8ce2276ddecfcd6ffb15e Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Thu, 28 Sep 2017 15:39:45 -0400 Subject: 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 ++++++- 9 files changed, 167 insertions(+), 29 deletions(-) (limited to 'gcc/cp') diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 2936f22..6e2e3a8 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 99a7b77..05dc8bb 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 a634901..f56c951 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 03e91b7..29d6c59 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 8bd341b..f5c8e80 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 b01d662..4bc0755 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 2bdac6d..0dae10e 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 4e87e47..d96423f 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 028d56f..326721e 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; } -- cgit v1.1