* C++ PATCH for c++/56973 (DR 696), lambda capture of const variables @ 2017-09-28 19:39 Jason Merrill 2017-10-10 18:07 ` Jason Merrill 0 siblings, 1 reply; 3+ messages in thread From: Jason Merrill @ 2017-09-28 19:39 UTC (permalink / raw) To: gcc-patches List [-- Attachment #1: Type: text/plain, Size: 827 bytes --] The G++ lambda implementation previously implemented an early tentative resolution of DR 696, whereby mentions of an outer constant variable would immediately decay to the constant value of that variable. But the final resolution specified that we should capture or not depending on how the variable is used: if we use it as an lvalue, it's captured; if we use it as an rvalue, it isn't. The first patch is some minor fixes discovered during this work. The second patch reworks how we find capture proxies to use local_specializations instead of name lookup. The third patch delays capture of constant variables until mark_rvalue_use/mark_lvalue_use. The third patch also adds calls to mark_*_use in a couple of places that needed it; I expect more will be necessary as well. Tested x86_64-pc-linux-gnu, applying to trunk. [-- Attachment #2: lfix.diff --] [-- Type: text/plain, Size: 2766 bytes --] commit ea50e0c044c8405aa7bc277628e38f838f1e66a7 Author: Jason Merrill <jason@redhat.com> Date: Wed Sep 27 17:06:31 2017 -0400 Small lambda fixes. * call.c (build_special_member_call): Use the return value of mark_lvalue_use. * decl.c (compute_array_index_type): Likewise. * parser.c (cp_parser_oacc_wait_list): Likewise. * lambda.c (is_normal_capture_proxy): Handle *this capture. (add_capture): Clarify internal_error message. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index e83cf99dc89..99a7b77efb2 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -8845,7 +8845,7 @@ build_special_member_call (tree instance, tree name, vec<tree, va_gc> **args, && (flags & LOOKUP_DELEGATING_CONS)) check_self_delegation (arg); /* Avoid change of behavior on Wunused-var-2.C. */ - mark_lvalue_use (instance); + instance = mark_lvalue_use (instance); return build2 (INIT_EXPR, class_type, instance, arg); } } diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 50fa1ba402e..ce45c1140d6 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -9329,7 +9329,7 @@ compute_array_index_type (tree name, tree size, tsubst_flags_t complain) { tree type = TREE_TYPE (size); - mark_rvalue_use (size); + size = mark_rvalue_use (size); if (cxx_dialect < cxx11 && TREE_CODE (size) == NOP_EXPR && TREE_SIDE_EFFECTS (size)) diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index e4412569a61..695666abbe3 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -283,6 +283,8 @@ is_normal_capture_proxy (tree decl) if (val == error_mark_node) return true; + if (TREE_CODE (val) == ADDR_EXPR) + val = TREE_OPERAND (val, 0); gcc_assert (TREE_CODE (val) == COMPONENT_REF); val = TREE_OPERAND (val, 1); return DECL_NORMAL_CAPTURE_P (val); @@ -592,7 +594,8 @@ add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p, && current_class_type == LAMBDA_EXPR_CLOSURE (lambda)) { if (COMPLETE_TYPE_P (current_class_type)) - internal_error ("trying to capture %qD after closure is complete", id); + internal_error ("trying to capture %qD in instantiation of " + "generic lambda", id); finish_member_declaration (member); } diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index f9b6f278afb..bb2a8774aa0 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -31712,7 +31712,7 @@ cp_parser_oacc_wait_list (cp_parser *parser, location_t clause_loc, tree list) { tree c = build_omp_clause (clause_loc, OMP_CLAUSE_WAIT); - mark_rvalue_use (targ); + targ = mark_rvalue_use (targ); OMP_CLAUSE_DECL (c) = targ; OMP_CLAUSE_CHAIN (c) = list; list = c; [-- Attachment #3: local-prox.diff --] [-- Type: text/plain, Size: 9341 bytes --] commit ea60e04977765a8041e1cba59a8d3028982c55e0 Author: Jason Merrill <jason@redhat.com> Date: Wed Sep 27 17:08:56 2017 -0400 Use local_specializations to find capture proxies. * cp-tree.h (DECL_CAPTURED_VARIABLE): New. * lambda.c (build_capture_proxy): Set it. (add_capture): Pass initializer to build_capture_proxy. (start_lambda_function): Likewise. (insert_capture_proxy): Use register_local_specialization. (is_lambda_ignored_entity): Always ignore proxies. * name-lookup.c (qualify_lookup): Don't check is_lambda_ignored_entity if LOOKUP_HIDDEN is set. * semantics.c (process_outer_var_ref): Use retrieve_local_specialization. * parser.c (cp_parser_lambda_body): Push local_specializations. * pt.c (tsubst_expr): Pass LOOKUP_HIDDEN when looking for a proxy. (tsubst_lambda_expr): Push local_specializations sooner. (tsubst_copy_and_build): Don't register_local_specialization. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 7c1c54c78b5..a6349019543 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -2471,10 +2471,12 @@ struct GTY(()) lang_decl_min { union lang_decl_u2 { /* In a FUNCTION_DECL for which DECL_THUNK_P holds, this is THUNK_VIRTUAL_OFFSET. + In a VAR_DECL for which DECL_HAS_VALUE_EXPR_P holds, + this is DECL_CAPTURED_VARIABLE. Otherwise this is DECL_ACCESS. */ tree GTY ((tag ("0"))) access; - /* For VAR_DECL in function, this is DECL_DISCRIMINATOR. */ + /* For TREE_STATIC VAR_DECL in function, this is DECL_DISCRIMINATOR. */ int GTY ((tag ("1"))) discriminator; } GTY ((desc ("%0.u.base.u2sel"))) u2; }; @@ -3240,6 +3242,10 @@ extern void decl_shadowed_for_var_insert (tree, tree); (DECL_LANG_SPECIFIC (VAR_TEMPL_TYPE_FIELD_OR_FUNCTION_DECL_CHECK (NODE)) \ ->u.min.template_info) +/* For a lambda capture proxy, its captured variable. */ +#define DECL_CAPTURED_VARIABLE(NODE) \ + (LANG_DECL_U2_CHECK (NODE, 0)->access) + /* For a VAR_DECL, indicates that the variable is actually a non-static data member of anonymous union that has been promoted to variable status. */ @@ -6793,7 +6799,7 @@ extern tree lambda_function (tree); extern void apply_deduced_return_type (tree, tree); extern tree add_capture (tree, tree, tree, bool, bool); extern tree add_default_capture (tree, tree, tree); -extern tree build_capture_proxy (tree); +extern tree build_capture_proxy (tree, tree); extern void insert_capture_proxy (tree); extern void insert_pending_capture_proxies (void); extern bool is_capture_proxy (tree); diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index 695666abbe3..66d510e6818 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -296,6 +296,9 @@ is_normal_capture_proxy (tree decl) void insert_capture_proxy (tree var) { + if (is_normal_capture_proxy (var)) + register_local_specialization (var, DECL_CAPTURED_VARIABLE (var)); + /* Put the capture proxy in the extra body block so that it won't clash with a later local variable. */ pushdecl_outermost_localscope (var); @@ -364,7 +367,7 @@ lambda_proxy_type (tree ref) debugging. */ tree -build_capture_proxy (tree member) +build_capture_proxy (tree member, tree init) { tree var, object, fn, closure, name, lam, type; @@ -414,6 +417,29 @@ build_capture_proxy (tree member) TREE_USED (var) = 1; DECL_CONTEXT (var) = fn; + if (DECL_NORMAL_CAPTURE_P (member)) + { + if (DECL_VLA_CAPTURE_P (member)) + { + init = CONSTRUCTOR_ELT (init, 0)->value; + init = TREE_OPERAND (init, 0); // Strip ADDR_EXPR. + init = TREE_OPERAND (init, 0); // Strip ARRAY_REF. + } + else + { + if (PACK_EXPANSION_P (init)) + init = PACK_EXPANSION_PATTERN (init); + if (TREE_CODE (init) == INDIRECT_REF) + init = TREE_OPERAND (init, 0); + STRIP_NOPS (init); + } + gcc_assert (VAR_P (init) || TREE_CODE (init) == PARM_DECL); + while (is_normal_capture_proxy (init)) + init = DECL_CAPTURED_VARIABLE (init); + retrofit_lang_decl (var); + DECL_CAPTURED_VARIABLE (var) = init; + } + if (name == this_identifier) { gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (lam) == member); @@ -609,7 +635,7 @@ add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p, = tree_cons (listmem, initializer, LAMBDA_EXPR_CAPTURE_LIST (lambda)); if (LAMBDA_EXPR_CLOSURE (lambda)) - return build_capture_proxy (member); + return build_capture_proxy (member, initializer); /* For explicit captures we haven't started the function yet, so we wait and build the proxy from cp_parser_lambda_body. */ return NULL_TREE; @@ -1243,8 +1269,8 @@ lambda_static_thunk_p (tree fn) bool is_lambda_ignored_entity (tree val) { - /* In unevaluated context, look past normal capture proxies. */ - if (cp_unevaluated_operand && is_normal_capture_proxy (val)) + /* Look past normal capture proxies. */ + if (is_normal_capture_proxy (val)) return true; /* Always ignore lambda fields, their names are only for debugging. */ @@ -1325,7 +1351,7 @@ start_lambda_function (tree fco, tree lambda_expr) /* Push the proxies for any explicit captures. */ for (tree cap = LAMBDA_EXPR_CAPTURE_LIST (lambda_expr); cap; cap = TREE_CHAIN (cap)) - build_capture_proxy (TREE_PURPOSE (cap)); + build_capture_proxy (TREE_PURPOSE (cap), TREE_VALUE (cap)); return body; } diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index a3a124b9ce2..6763a5b9c68 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -5296,7 +5296,7 @@ qualify_lookup (tree val, int flags) if (flags & (LOOKUP_PREFER_NAMESPACES | LOOKUP_PREFER_TYPES)) return false; /* Look through lambda things that we shouldn't be able to see. */ - if (is_lambda_ignored_entity (val)) + if (!(flags & LOOKUP_HIDDEN) && is_lambda_ignored_entity (val)) return false; return true; } diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index bb2a8774aa0..f22c2c091dc 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -10560,6 +10560,8 @@ cp_parser_lambda_body (cp_parser* parser, tree lambda_expr) + function_definition_after_declarator + ctor_initializer_opt_and_function_body */ { + local_specialization_stack s (lss_copy); + tree fco = lambda_function (lambda_expr); tree body = start_lambda_function (fco, lambda_expr); bool done = false; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index f12ab2605d8..2bdac6de6c4 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -15986,7 +15986,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, { /* We're in tsubst_lambda_expr, we've already inserted a new capture proxy, so look it up and register it. */ - tree inst = lookup_name (DECL_NAME (decl)); + tree inst = lookup_name_real (DECL_NAME (decl), 0, 0, + /*block_p=*/true, 0, LOOKUP_HIDDEN); gcc_assert (inst != decl && is_capture_proxy (inst)); register_local_specialization (inst, decl); break; @@ -16906,10 +16907,10 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) if (nested) push_function_context (); - tree body = start_lambda_function (fn, r); - local_specialization_stack s (lss_copy); + tree body = start_lambda_function (fn, r); + register_parameter_specializations (oldfn, fn); tsubst_expr (DECL_SAVED_TREE (oldfn), args, complain, r, @@ -18136,11 +18137,7 @@ tsubst_copy_and_build (tree t, r = build_cxx_call (wrap, 0, NULL, tf_warning_or_error); } else if (outer_automatic_var_p (r)) - { - r = process_outer_var_ref (r, complain); - if (is_capture_proxy (r) && !DECL_PACK_P (t)) - register_local_specialization (r, t); - } + r = process_outer_var_ref (r, complain); if (TREE_CODE (TREE_TYPE (t)) != REFERENCE_TYPE) /* If the original type was a reference, we'll be wrapped in diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 3a3ae55aa44..4e87e47d9b3 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -3303,16 +3303,19 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain) if (parsing_nsdmi ()) containing_function = NULL_TREE; - if (containing_function && DECL_TEMPLATE_INFO (context) - && LAMBDA_FUNCTION_P (containing_function)) + if (containing_function && LAMBDA_FUNCTION_P (containing_function)) { - /* Check whether we've already built a proxy; - insert_pending_capture_proxies doesn't update - local_specializations. */ - tree d = lookup_name (DECL_NAME (decl)); - if (d && is_capture_proxy (d) - && DECL_CONTEXT (d) == containing_function) - return d; + /* Check whether we've already built a proxy. */ + tree d = retrieve_local_specialization (decl); + if (d && is_capture_proxy (d)) + { + if (DECL_CONTEXT (d) == containing_function) + /* We already have an inner proxy. */ + return d; + else + /* We need to capture an outer proxy. */ + return process_outer_var_ref (d, complain); + } } /* If we are in a lambda function, we can move out until we hit [-- Attachment #4: 56973.diff --] [-- Type: text/plain, Size: 13330 bytes --] commit 3a8f9701bcfa6a593f720dc783155db3ab510c92 Author: Jason Merrill <jason@redhat.com> Date: Wed Sep 27 17:09:13 2017 -0400 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. 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<TYPE>(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 <typename T> void bar (T) { constexpr auto N = a<1>; auto f = [&] (auto i) { - static_assert (static_cast<int>(N) == 1, ""); + return static_cast<int>(N) == 1; }; foo (f); } ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: C++ PATCH for c++/56973 (DR 696), lambda capture of const variables 2017-09-28 19:39 C++ PATCH for c++/56973 (DR 696), lambda capture of const variables Jason Merrill @ 2017-10-10 18:07 ` Jason Merrill 2017-11-13 23:14 ` Jason Merrill 0 siblings, 1 reply; 3+ messages in thread From: Jason Merrill @ 2017-10-10 18:07 UTC (permalink / raw) To: gcc-patches List [-- Attachment #1: Type: text/plain, Size: 1425 bytes --] On Thu, Sep 28, 2017 at 3:38 PM, Jason Merrill <jason@redhat.com> wrote: > The G++ lambda implementation previously implemented an early > tentative resolution of DR 696, whereby mentions of an outer constant > variable would immediately decay to the constant value of that > variable. But the final resolution specified that we should capture > or not depending on how the variable is used: if we use it as an > lvalue, it's captured; if we use it as an rvalue, it isn't. > > The first patch is some minor fixes discovered during this work. > The second patch reworks how we find capture proxies to use > local_specializations instead of name lookup. > The third patch delays capture of constant variables until > mark_rvalue_use/mark_lvalue_use. > > The third patch also adds calls to mark_*_use in a couple of places > that needed it; I expect more will be necessary as well. I tested using delayed capture for all variables, and these fixes are the result. The first two patches are fixes for generic issues that I came across while looking at the capture issues. The first adds checking within a template definition for non-dependent return statements. The second fixes a few small issues. The third patch implements generic lambda capture in dependent full-expressions, and the fourth adds some missing mark_*_use calls and fixes other issues with delayed capture. Tested x86_64-pc-linux-gnu, applying to trunk. [-- Attachment #2: nd-return.diff --] [-- Type: text/plain, Size: 4033 bytes --] commit 06873162a116c6629dea55a36f8b43f71c6c7880 Author: Jason Merrill <jason@redhat.com> Date: Tue Oct 10 10:49:07 2017 -0400 Check non-dependent conversion in return from template fn. * typeck.c (check_return_expr): Check non-dependent conversion in templates. * constraint.cc (check_function_concept): Don't complain about an empty concept if seen_error. diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 64a8ea926d2..8b49455a526 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -2504,7 +2504,12 @@ check_function_concept (tree fn) { location_t loc = DECL_SOURCE_LOCATION (fn); if (TREE_CODE (body) == STATEMENT_LIST && !STATEMENT_LIST_HEAD (body)) - error_at (loc, "definition of concept %qD is empty", fn); + { + if (seen_error ()) + /* The definition was probably erroneous, not empty. */; + else + error_at (loc, "definition of concept %qD is empty", fn); + } else error_at (loc, "definition of concept %qD has multiple statements", fn); } diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index c3310db7b3b..01647d04bc8 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -8957,10 +8957,14 @@ check_return_expr (tree retval, bool *no_warning) if (check_for_bare_parameter_packs (retval)) return error_mark_node; - if (WILDCARD_TYPE_P (TREE_TYPE (DECL_RESULT (current_function_decl))) + /* If one of the types might be void, we can't tell whether we're + returning a value. */ + if ((WILDCARD_TYPE_P (TREE_TYPE (DECL_RESULT (current_function_decl))) + && !current_function_auto_return_pattern) || (retval != NULL_TREE - && type_dependent_expression_p (retval))) - return retval; + && (TREE_TYPE (retval) == NULL_TREE + || WILDCARD_TYPE_P (TREE_TYPE (retval))))) + goto dependent; } functype = TREE_TYPE (TREE_TYPE (current_function_decl)); @@ -9098,8 +9102,10 @@ check_return_expr (tree retval, bool *no_warning) warning (OPT_Weffc__, "%<operator=%> should return a reference to %<*this%>"); } - if (processing_template_decl) + if (dependent_type_p (functype) + || type_dependent_expression_p (retval)) { + dependent: /* We should not have changed the return value. */ gcc_assert (retval == saved_retval); return retval; @@ -9126,6 +9132,7 @@ check_return_expr (tree retval, bool *no_warning) named_return_value_okay_p = (retval != NULL_TREE + && !processing_template_decl /* Must be a local, automatic variable. */ && VAR_P (retval) && DECL_CONTEXT (retval) == current_function_decl @@ -9222,6 +9229,9 @@ check_return_expr (tree retval, bool *no_warning) build_zero_cst (TREE_TYPE (retval))); } + if (processing_template_decl) + return saved_retval; + /* Actually copy the value returned into the appropriate location. */ if (retval && retval != result) retval = build2 (INIT_EXPR, TREE_TYPE (result), result, retval); diff --git a/gcc/testsuite/g++.dg/concepts/req6.C b/gcc/testsuite/g++.dg/concepts/req6.C index 670fd542f6f..50fa3b4dadd 100644 --- a/gcc/testsuite/g++.dg/concepts/req6.C +++ b/gcc/testsuite/g++.dg/concepts/req6.C @@ -4,7 +4,7 @@ struct X { }; int operator==(X, X) { return 0; } template<typename T> - concept bool C1() { return X(); } + concept bool C1() { return X(); } // { dg-error "bool" } template<C1 T> void h(T) { } // OK until used. diff --git a/gcc/testsuite/g++.old-deja/g++.pt/crash3.C b/gcc/testsuite/g++.old-deja/g++.pt/crash3.C index 160cbe541a1..e5b3f25b530 100644 --- a/gcc/testsuite/g++.old-deja/g++.pt/crash3.C +++ b/gcc/testsuite/g++.old-deja/g++.pt/crash3.C @@ -6,11 +6,11 @@ public: CVector<int> f() const { CVector<int> v(); - return v; + return v; // { dg-error "convert" } } CVector<long> g() const { CVector<long> v(); - return v; + return v; // { dg-error "convert" } } }; [-- Attachment #3: misc.diff --] [-- Type: text/plain, Size: 5905 bytes --] commit 64682966e442653c4cfdb7606a4f09258762d663 Author: Jason Merrill <jason@redhat.com> Date: Thu Oct 5 15:56:12 2017 -0400 Various small C++ fixes. * typeck.c (condition_conversion): Assert !processing_template_decl. * semantics.c (finish_omp_clauses): Don't fold_build_cleanup_point_expr if processing_template_decl. (outer_var_p): A temporary can't be from an outer scope. * pt.c (type_dependent_expression_p): Fix dependency checking of functions without DECL_TEMPLATE_INFO. (instantiate_decl): Use lss_copy. * constexpr.c (is_valid_constexpr_fn): Fix lambdas before C++17. diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 8a5be2079d8..f279f707676 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -196,7 +196,14 @@ is_valid_constexpr_fn (tree fun, bool complain) } } - if (!DECL_CONSTRUCTOR_P (fun)) + if (LAMBDA_TYPE_P (CP_DECL_CONTEXT (fun)) && cxx_dialect < cxx17) + { + ret = false; + if (complain) + inform (DECL_SOURCE_LOCATION (fun), + "lambdas are implicitly constexpr only in C++17 and later"); + } + else if (!DECL_CONSTRUCTOR_P (fun)) { tree rettype = TREE_TYPE (TREE_TYPE (fun)); if (!literal_type_p (rettype)) diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 52fc4d6a222..d93d518c22d 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -23224,15 +23224,9 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p) synthesize_method (d); else if (TREE_CODE (d) == FUNCTION_DECL) { - hash_map<tree, tree> *saved_local_specializations; - tree block = NULL_TREE; - - /* Save away the current list, in case we are instantiating one - template from within the body of another. */ - saved_local_specializations = local_specializations; - /* Set up the list of local specializations. */ - local_specializations = new hash_map<tree, tree>; + local_specialization_stack lss (push_to_top ? lss_blank : lss_copy); + tree block = NULL_TREE; /* Set up context. */ if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern) @@ -23271,10 +23265,6 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p) = DECL_STRUCT_FUNCTION (code_pattern)->language->infinite_loop; } - /* We don't need the local specializations any more. */ - delete local_specializations; - local_specializations = saved_local_specializations; - /* Finish the function. */ if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern) && TREE_CODE (DECL_CONTEXT (code_pattern)) == FUNCTION_DECL) @@ -24307,21 +24297,22 @@ type_dependent_expression_p (tree expression) && (any_dependent_template_arguments_p (INNERMOST_TEMPLATE_ARGS (DECL_TI_ARGS (expression))))) return true; + } - /* Otherwise, if the decl isn't from a dependent scope, it can't be - type-dependent. Checking this is important for functions with auto - return type, which looks like a dependent type. */ - if (TREE_CODE (expression) == FUNCTION_DECL - && (!DECL_CLASS_SCOPE_P (expression) - || !dependent_type_p (DECL_CONTEXT (expression))) - && (!DECL_FRIEND_CONTEXT (expression) - || !dependent_type_p (DECL_FRIEND_CONTEXT (expression))) - && !DECL_LOCAL_FUNCTION_P (expression)) - { - gcc_assert (!dependent_type_p (TREE_TYPE (expression)) - || undeduced_auto_decl (expression)); - return false; - } + /* Otherwise, if the function decl isn't from a dependent scope, it can't be + type-dependent. Checking this is important for functions with auto return + type, which looks like a dependent type. */ + if (TREE_CODE (expression) == FUNCTION_DECL + && !(DECL_CLASS_SCOPE_P (expression) + && dependent_type_p (DECL_CONTEXT (expression))) + && !(DECL_FRIEND_P (expression) + && (!DECL_FRIEND_CONTEXT (expression) + || dependent_type_p (DECL_FRIEND_CONTEXT (expression)))) + && !DECL_LOCAL_FUNCTION_P (expression)) + { + gcc_assert (!dependent_type_p (TREE_TYPE (expression)) + || undeduced_auto_decl (expression)); + return false; } /* Always dependent, on the number of arguments if nothing else. */ diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index d96423f2348..d4097ffd238 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -3265,6 +3265,8 @@ outer_var_p (tree decl) { return ((VAR_P (decl) || TREE_CODE (decl) == PARM_DECL) && DECL_FUNCTION_SCOPE_P (decl) + /* Don't get confused by temporaries. */ + && DECL_NAME (decl) && (DECL_CONTEXT (decl) != current_function_decl || parsing_nsdmi ())); } @@ -6213,8 +6215,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort) "positive"); t = integer_one_node; } + t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); } - t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); } OMP_CLAUSE_OPERAND (c, 1) = t; } @@ -7095,8 +7097,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort) "integral constant"); remove = true; } + t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); } - t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); } /* Update list item. */ diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 01647d04bc8..90a1f47e3fd 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -5603,8 +5603,9 @@ tree condition_conversion (tree expr) { tree t; - if (processing_template_decl) - return expr; + /* Anything that might happen in a template should go through + maybe_convert_cond. */ + gcc_assert (!processing_template_decl); t = perform_implicit_conversion_flags (boolean_type_node, expr, tf_warning_or_error, LOOKUP_NORMAL); t = fold_build_cleanup_point_expr (boolean_type_node, t); [-- Attachment #4: dep-cap.diff --] [-- Type: text/plain, Size: 9050 bytes --] commit b32befdb7e294788fe9c0cce2288d0d2d9c61d7a Author: Jason Merrill <jason@redhat.com> Date: Fri Sep 29 13:15:21 2017 -0400 Handle generic lambda capture in dependent expressions. * lambda.c (need_generic_capture, dependent_capture_r) (do_dependent_capture): New. * pt.c (processing_nonlambda_template): Use need_generic_capture. * semantics.c (maybe_cleanup_point_expr) (maybe_cleanup_point_expr_void, finish_goto_stmt) (maybe_convert_cond): Call do_dependent_capture. * typeck.c (build_static_cast): Remove dependent capture handling. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index b29e4e0be02..482c977f6b4 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6432,6 +6432,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 need_generic_capture (void); extern bool processing_nonlambda_template (void); extern tree instantiate_class_template (tree); extern tree instantiate_template (tree, tree, tsubst_flags_t); @@ -6833,6 +6834,7 @@ extern tree current_nonlambda_function (void); extern tree nonlambda_method_basetype (void); extern tree current_nonlambda_scope (void); extern bool generic_lambda_fn_p (tree); +extern tree do_dependent_capture (tree, bool = false); extern bool lambda_fn_in_template_p (tree); extern void maybe_add_lambda_conv_op (tree); extern bool is_lambda_ignored_entity (tree); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 0e70bb5d59d..515e22493ff 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -6867,6 +6867,8 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, DECL_INITIAL (decl) = NULL_TREE; } + init = do_dependent_capture (init); + /* Generally, initializers in templates are expanded when the template is instantiated. But, if DECL is a variable constant then it can be used in future constant expressions, so its value diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index 78bd89782aa..6f611487ddb 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -977,6 +977,121 @@ generic_lambda_fn_p (tree callop) && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (callop))); } +/* Returns true iff we need to consider default capture for an enclosing + generic lambda. */ + +bool +need_generic_capture (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))) + { + tree lam = CLASSTYPE_LAMBDA_EXPR (t); + if (!lam || LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lam) == CPLD_NONE) + /* No default capture. */ + break; + outer_closure = t; + } + + if (!outer_closure) + /* No lambda. */ + return false; + else if (dependent_type_p (outer_closure)) + /* The enclosing context isn't instantiated. */ + return false; + else + return true; +} + +/* A lambda-expression...is said to implicitly capture the entity...if the + compound-statement...names the entity in a potentially-evaluated + expression where the enclosing full-expression depends on a generic lambda + parameter declared within the reaching scope of the lambda-expression. */ + +static tree +dependent_capture_r (tree *tp, int *walk_subtrees, void *data) +{ + hash_set<tree> *pset = (hash_set<tree> *)data; + + if (TYPE_P (*tp)) + *walk_subtrees = 0; + + if (outer_automatic_var_p (*tp)) + { + tree t = process_outer_var_ref (*tp, tf_warning_or_error, /*force*/true); + if (t != *tp + && TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE + && TREE_CODE (TREE_TYPE (*tp)) != REFERENCE_TYPE) + t = convert_from_reference (t); + *tp = t; + } + + if (pset->add (*tp)) + *walk_subtrees = 0; + + switch (TREE_CODE (*tp)) + { + /* Don't walk into unevaluated context or another lambda. */ + case SIZEOF_EXPR: + case ALIGNOF_EXPR: + case TYPEID_EXPR: + case NOEXCEPT_EXPR: + case LAMBDA_EXPR: + *walk_subtrees = 0; + break; + + /* Don't walk into statements whose subexpressions we already + handled. */ + case TRY_BLOCK: + case EH_SPEC_BLOCK: + case HANDLER: + case IF_STMT: + case FOR_STMT: + case RANGE_FOR_STMT: + case WHILE_STMT: + case DO_STMT: + case SWITCH_STMT: + case STATEMENT_LIST: + case RETURN_EXPR: + *walk_subtrees = 0; + break; + + case DECL_EXPR: + { + tree decl = DECL_EXPR_DECL (*tp); + if (VAR_P (decl)) + { + /* walk_tree_1 won't step in here. */ + cp_walk_tree (&DECL_INITIAL (decl), + dependent_capture_r, &pset, NULL); + *walk_subtrees = 0; + } + } + break; + + default: + break; + } + + return NULL_TREE; +} + +tree +do_dependent_capture (tree expr, bool force) +{ + if (!need_generic_capture () + || (!force && !instantiation_dependent_expression_p (expr))) + return expr; + + hash_set<tree> pset; + cp_walk_tree (&expr, dependent_capture_r, &pset, NULL); + return expr; +} + /* If the closure TYPE has a static op(), also add a conversion to function pointer. */ diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index d93d518c22d..78748a91a07 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -9494,30 +9494,14 @@ 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. */ +/* Returns true iff we are currently within a template other than a + default-capturing generic lambda, so we don't need to worry about semantic + processing. */ 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; + return processing_template_decl && !need_generic_capture (); } /* Returns true if T depends on any template parameter with level LEVEL. */ diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index d4097ffd238..eb36b3029f1 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -410,6 +410,8 @@ maybe_cleanup_point_expr (tree expr) { if (!processing_template_decl && stmts_are_full_exprs_p ()) expr = fold_build_cleanup_point_expr (TREE_TYPE (expr), expr); + else + expr = do_dependent_capture (expr); return expr; } @@ -423,6 +425,8 @@ maybe_cleanup_point_expr_void (tree expr) { if (!processing_template_decl && stmts_are_full_exprs_p ()) expr = fold_build_cleanup_point_expr (void_type_node, expr); + else + expr = do_dependent_capture (expr); return expr; } @@ -629,6 +633,8 @@ finish_goto_stmt (tree destination) = fold_build_cleanup_point_expr (TREE_TYPE (destination), destination); } + else + destination = do_dependent_capture (destination); } check_goto (destination); @@ -650,7 +656,7 @@ maybe_convert_cond (tree cond) /* Wait until we instantiate templates before doing conversion. */ if (processing_template_decl) - return cond; + return do_dependent_capture (cond); if (warn_sequence_point) verify_sequence_points (cond); diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 90a1f47e3fd..fc114b8f592 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -7059,11 +7059,7 @@ build_static_cast (tree type, tree oexpr, tsubst_flags_t complain) if (dependent) { tmpl: - expr = oexpr; - if (dependent) - /* Handle generic lambda capture. */ - expr = mark_lvalue_use (expr); - expr = build_min (STATIC_CAST_EXPR, type, expr); + expr = build_min (STATIC_CAST_EXPR, type, oexpr); /* We don't know if it will or will not have side effects. */ TREE_SIDE_EFFECTS (expr) = 1; return convert_from_reference (expr); @@ -9109,7 +9105,7 @@ check_return_expr (tree retval, bool *no_warning) dependent: /* We should not have changed the return value. */ gcc_assert (retval == saved_retval); - return retval; + return do_dependent_capture (retval, /*force*/true); } /* The fabled Named Return Value optimization, as per [class.copy]/15: diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-dep2.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-dep2.C new file mode 100644 index 00000000000..91e3804cb0b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-dep2.C @@ -0,0 +1,18 @@ +// { dg-do compile { target c++14 } } + +struct A { void operator()(int) const {} }; + +template <class T> +void f() +{ + constexpr A a {}; + + [=](auto b) { + a(b); + }(42); +} + +int main() +{ + f<int>(); +} [-- Attachment #5: cap-fix.diff --] [-- Type: text/plain, Size: 14345 bytes --] commit a3370f6e8c39a98f9c1dffa72d27e92f9afc8271 Author: Jason Merrill <jason@redhat.com> Date: Tue Oct 10 12:40:32 2017 -0400 More delayed lambda capture fixes. * call.c (add_function_candidate): Use build_address. (build_op_call_1): Call mark_lvalue_use early. (build_over_call): Handle error from build_this. * constexpr.c (cxx_bind_parameters_in_call): Use build_address. (cxx_eval_increment_expression): Don't use rvalue(). * cvt.c (convert_to_void): Use mark_discarded_use. * expr.c (mark_use): Handle PARM_DECL, NON_DEPENDENT_EXPR. Fix reference handling. Don't copy the expression. (mark_discarded_use): New. * lambda.c (insert_capture_proxy): Add some sanity checking. (maybe_add_lambda_conv_op): Set cp_unevaluated_operand. * pt.c (register_local_specialization): Add sanity check. * semantics.c (process_outer_var_ref): Fix check for existing proxy. * typeck.c (cp_build_addr_expr_1): Handle error from mark_lvalue_use. (cp_build_modify_expr): Call mark_lvalue_use_nonread, handle error from rvalue. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 9d747be9d79..13269024547 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -2160,7 +2160,10 @@ add_function_candidate (struct z_candidate **candidates, else { parmtype = build_pointer_type (parmtype); - arg = build_this (arg); + /* We don't use build_this here because we don't want to + capture the object argument until we've chosen a + non-static member function. */ + arg = build_address (arg); argtype = lvalue_type (arg); } } @@ -4446,14 +4449,17 @@ build_op_call_1 (tree obj, vec<tree, va_gc> **args, tsubst_flags_t complain) { struct z_candidate *candidates = 0, *cand; tree fns, convs, first_mem_arg = NULL_TREE; - tree type = TREE_TYPE (obj); bool any_viable_p; tree result = NULL_TREE; void *p; + obj = mark_lvalue_use (obj); + if (error_operand_p (obj)) return error_mark_node; + tree type = TREE_TYPE (obj); + obj = prep_operand (obj); if (TYPE_PTRMEMFUNC_P (type)) @@ -7772,6 +7778,9 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) tree converted_arg; tree base_binfo; + if (arg == error_mark_node) + return error_mark_node; + if (convs[i]->bad_p) { if (complain & tf_error) diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index f279f707676..59192829d71 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1268,7 +1268,10 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, && is_dummy_object (x)) { x = ctx->object; - x = cp_build_addr_expr (x, tf_warning_or_error); + /* We don't use cp_build_addr_expr here because we don't want to + capture the object argument until we've chosen a non-static member + function. */ + x = build_address (x); } bool lval = false; arg = cxx_eval_constant_expression (ctx, x, lval, @@ -3642,9 +3645,9 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t, non_constant_p, overflow_p); /* The operand as an rvalue. */ - tree val = rvalue (op); - val = cxx_eval_constant_expression (ctx, val, false, - non_constant_p, overflow_p); + tree val + = cxx_eval_constant_expression (ctx, op, false, + non_constant_p, overflow_p); /* Don't VERIFY_CONSTANT if this might be dealing with a pointer to a local array in a constexpr function. */ bool ptr = POINTER_TYPE_P (TREE_TYPE (val)); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 482c977f6b4..1c008dbb3b3 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6270,6 +6270,7 @@ extern tree mark_rvalue_use (tree, extern tree mark_lvalue_use (tree); extern tree mark_lvalue_use_nonread (tree); extern tree mark_type_use (tree); +extern tree mark_discarded_use (tree); extern void mark_exp_read (tree); /* friend.c */ diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c index a3bd4a137d8..57cde632cc8 100644 --- a/gcc/cp/cvt.c +++ b/gcc/cp/cvt.c @@ -1055,24 +1055,10 @@ convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain) || TREE_TYPE (expr) == error_mark_node) return error_mark_node; + expr = mark_discarded_use (expr); if (implicit == ICV_CAST) + /* An explicit cast to void avoids all -Wunused-but-set* warnings. */ mark_exp_read (expr); - else - { - tree exprv = expr; - - while (TREE_CODE (exprv) == COMPOUND_EXPR) - exprv = TREE_OPERAND (exprv, 1); - if (DECL_P (exprv) - || handled_component_p (exprv) - || INDIRECT_REF_P (exprv)) - /* Expr is not being 'used' here, otherwise we whould have - called mark_{rl}value_use use here, which would have in turn - called mark_exp_read. Rather, we call mark_exp_read directly - to avoid some warnings when - -Wunused-but-set-{variable,parameter} is in effect. */ - mark_exp_read (exprv); - } if (!TREE_TYPE (expr)) return expr; diff --git a/gcc/cp/expr.c b/gcc/cp/expr.c index f5c8e801918..23e30cf789c 100644 --- a/gcc/cp/expr.c +++ b/gcc/cp/expr.c @@ -96,16 +96,21 @@ mark_use (tree expr, bool rvalue_p, bool read_p, { #define RECUR(t) mark_use ((t), rvalue_p, read_p, loc, reject_builtin) + if (expr == NULL_TREE || expr == error_mark_node) + return expr; + if (reject_builtin && reject_gcc_builtin (expr, loc)) return error_mark_node; if (read_p) mark_exp_read (expr); + tree oexpr = expr; bool recurse_op[3] = { false, false, false }; switch (TREE_CODE (expr)) { case VAR_DECL: + case PARM_DECL: if (outer_automatic_var_p (expr) && decl_constant_var_p (expr)) { @@ -119,10 +124,13 @@ mark_use (tree expr, bool rvalue_p, bool read_p, } } expr = process_outer_var_ref (expr, tf_warning_or_error, true); - expr = convert_from_reference (expr); + if (!(TREE_TYPE (oexpr) + && TREE_CODE (TREE_TYPE (oexpr)) == REFERENCE_TYPE)) + expr = convert_from_reference (expr); } break; case COMPONENT_REF: + case NON_DEPENDENT_EXPR: recurse_op[0] = true; break; case COMPOUND_EXPR: @@ -140,35 +148,23 @@ mark_use (tree expr, bool rvalue_p, bool read_p, 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; - } + expr = convert_from_reference (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; + op = RECUR (op); + if (op == error_mark_node) + return error_mark_node; + TREE_OPERAND (expr, i) = op; } - 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 } @@ -187,6 +183,52 @@ mark_rvalue_use (tree e, return mark_use (e, true, true, loc, reject_builtin); } +/* Called when expr appears as a discarded-value expression. */ + +tree +mark_discarded_use (tree expr) +{ + /* The lvalue-to-rvalue conversion (7.1) is applied if and only if the + expression is a glvalue of volatile-qualified type and it is one of the + following: + * ( expression ), where expression is one of these expressions, + * id-expression (8.1.4), + * subscripting (8.2.1), + * class member access (8.2.5), + * indirection (8.3.1), + * pointer-to-member operation (8.5), + * conditional expression (8.16) where both the second and the third + operands are one of these expressions, or + * comma expression (8.19) where the right operand is one of these + expressions. */ + if (expr == NULL_TREE) + return expr; + + switch (TREE_CODE (expr)) + { + case COND_EXPR: + TREE_OPERAND (expr, 2) = mark_discarded_use (TREE_OPERAND (expr, 2)); + gcc_fallthrough (); + case COMPOUND_EXPR: + TREE_OPERAND (expr, 1) = mark_discarded_use (TREE_OPERAND (expr, 1)); + return expr; + + case COMPONENT_REF: + case ARRAY_REF: + case INDIRECT_REF: + case MEMBER_REF: + break; + default: + if (DECL_P (expr)) + break; + else + return expr; + } + + /* Like mark_rvalue_use, but don't reject built-ins. */ + return mark_use (expr, true, true, input_location, false); +} + /* Called whenever an expression is used in an lvalue context. */ tree diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index 6f611487ddb..76f2f29578f 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -297,7 +297,17 @@ void insert_capture_proxy (tree var) { if (is_normal_capture_proxy (var)) - register_local_specialization (var, DECL_CAPTURED_VARIABLE (var)); + { + tree cap = DECL_CAPTURED_VARIABLE (var); + if (CHECKING_P) + { + gcc_assert (!is_normal_capture_proxy (cap)); + tree old = retrieve_local_specialization (cap); + if (old) + gcc_assert (DECL_CONTEXT (old) != DECL_CONTEXT (var)); + } + register_local_specialization (var, cap); + } /* Put the capture proxy in the extra body block so that it won't clash with a later local variable. */ @@ -1188,7 +1198,10 @@ maybe_add_lambda_conv_op (tree type) if (generic_lambda_p) { + /* Avoid capturing variables in this context. */ + ++cp_unevaluated_operand; tree a = forward_parm (tgt); + --cp_unevaluated_operand; CALL_EXPR_ARG (call, ix) = a; if (decltype_call) diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 7883c64f33f..142a090cbab 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -11873,6 +11873,8 @@ cp_convert_range_for (tree statement, tree range_decl, tree range_expr, tree iter_type, begin_expr, end_expr; tree condition, expression; + range_expr = mark_lvalue_use (range_expr); + if (range_decl == error_mark_node || range_expr == error_mark_node) /* If an error happened previously do nothing or else a lot of unhelpful errors would be issued. */ diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 78748a91a07..ba52f3b57a6 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -1895,6 +1895,7 @@ reregister_specialization (tree spec, tree tinfo, tree new_spec) void register_local_specialization (tree spec, tree tmpl) { + gcc_assert (tmpl != spec); local_specializations->put (tmpl, spec); } diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index eb36b3029f1..f182006dfc2 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -3320,8 +3320,12 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, bool force_use) if (containing_function && LAMBDA_FUNCTION_P (containing_function)) { /* Check whether we've already built a proxy. */ - tree d = retrieve_local_specialization (decl); - if (d && is_capture_proxy (d)) + tree var = decl; + while (is_normal_capture_proxy (var)) + var = DECL_CAPTURED_VARIABLE (var); + tree d = retrieve_local_specialization (var); + + if (d && d != decl && is_capture_proxy (d)) { if (DECL_CONTEXT (d) == containing_function) /* We already have an inner proxy. */ diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index fc114b8f592..08b2ae555e6 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -5654,6 +5654,9 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain) return error_mark_node; arg = mark_lvalue_use (arg); + if (error_operand_p (arg)) + return error_mark_node; + argtype = lvalue_type (arg); gcc_assert (!(identifier_p (arg) && IDENTIFIER_ANY_OP_P (arg))); @@ -7698,6 +7701,8 @@ tree cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, tree rhs, tsubst_flags_t complain) { + lhs = mark_lvalue_use_nonread (lhs); + tree result = NULL_TREE; tree newrhs = rhs; tree lhstype = TREE_TYPE (lhs); @@ -7920,6 +7925,8 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, operator. -- end note ] */ lhs = cp_stabilize_reference (lhs); rhs = rvalue (rhs); + if (rhs == error_mark_node) + return error_mark_node; rhs = stabilize_expr (rhs, &init); newrhs = cp_build_binary_op (loc, modifycode, lhs, rhs, complain); if (newrhs == error_mark_node) diff --git a/gcc/testsuite/g++.dg/cpp0x/error1.C b/gcc/testsuite/g++.dg/cpp0x/error1.C index 33557f2f80b..115d800bb35 100644 --- a/gcc/testsuite/g++.dg/cpp0x/error1.C +++ b/gcc/testsuite/g++.dg/cpp0x/error1.C @@ -1,10 +1,17 @@ // PR c++/34395 // { dg-do compile { target c++11 } } -template<int... N> void foo (int... x[N]) // { dg-message "int \\\[N\\\]\\.\\.\\. x" } +void f(...); +template<int... N> void foo (int... x[N]) // { dg-message "declared here" } { struct A { - A () { x; } // { dg-error "use of parameter from containing function" } + A () { f(x...); } // { dg-error "use of parameter from containing function" } }; } + +int main() +{ + int ar[4]; + foo<4>(ar); +} diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-ice5.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-ice5.C index 473e412cb9d..88b7d1a05a1 100644 --- a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-ice5.C +++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-ice5.C @@ -12,7 +12,7 @@ using Void = void; template<typename F,typename A> auto -bar(F f, A a) -> decltype( ( f(a) , 0 ) ) // { dg-error "no match" } +bar(F f, A a) -> decltype( ( f(a) , 0 ) ) // { dg-message "" } { return {}; } diff --git a/gcc/testsuite/g++.dg/template/crash108.C b/gcc/testsuite/g++.dg/template/crash108.C index 221d80ee5f1..9bcabc6009b 100644 --- a/gcc/testsuite/g++.dg/template/crash108.C +++ b/gcc/testsuite/g++.dg/template/crash108.C @@ -1,5 +1,5 @@ // PR c++/50861 -template<class T> struct A {A(int b=k(0));}; // { dg-error "parameter|arguments" } +template<class T> struct A {A(int b=k(0));}; // { dg-error "parameter|argument" } void f(int k){A<int> a;} // // { dg-message "declared" } // { dg-message "note" "note" { target *-*-* } 3 } ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: C++ PATCH for c++/56973 (DR 696), lambda capture of const variables 2017-10-10 18:07 ` Jason Merrill @ 2017-11-13 23:14 ` Jason Merrill 0 siblings, 0 replies; 3+ messages in thread From: Jason Merrill @ 2017-11-13 23:14 UTC (permalink / raw) To: gcc-patches List [-- Attachment #1: Type: text/plain, Size: 1949 bytes --] On Tue, Oct 10, 2017 at 1:59 PM, Jason Merrill <jason@redhat.com> wrote: > On Thu, Sep 28, 2017 at 3:38 PM, Jason Merrill <jason@redhat.com> wrote: >> The G++ lambda implementation previously implemented an early >> tentative resolution of DR 696, whereby mentions of an outer constant >> variable would immediately decay to the constant value of that >> variable. But the final resolution specified that we should capture >> or not depending on how the variable is used: if we use it as an >> lvalue, it's captured; if we use it as an rvalue, it isn't. >> >> The first patch is some minor fixes discovered during this work. >> The second patch reworks how we find capture proxies to use >> local_specializations instead of name lookup. >> The third patch delays capture of constant variables until >> mark_rvalue_use/mark_lvalue_use. >> >> The third patch also adds calls to mark_*_use in a couple of places >> that needed it; I expect more will be necessary as well. > > I tested using delayed capture for all variables, and these fixes are > the result. > > The first two patches are fixes for generic issues that I came across > while looking at the capture issues. The first adds checking within a > template definition for non-dependent return statements. The second > fixes a few small issues. The third patch implements generic lambda > capture in dependent full-expressions, and the fourth adds some > missing mark_*_use calls and fixes other issues with delayed capture. P0588, voted in at the Albuquerque meeting last week, simplifies implicit capture such that we can immediately capture named variables and remove all the special generic lambda capture machinery. mark_*_use are kept largely intact, but will never cause a capture; they are now used for looking through capture proxies for constant variables, and for diagnostics about odr-use of un-captured outer variables. Tested x86_64-pc-linux-gnu, applying to trunk. Jason [-- Attachment #2: p0588.diff --] [-- Type: text/plain, Size: 12836 bytes --] commit d1730186f54ebc3fa82fe801cd3c500a9b8235af Author: Jason Merrill <jason@redhat.com> Date: Thu Nov 9 16:32:58 2017 -0500 Capture adjustments for P0588R1. * semantics.c (process_outer_var_ref): Capture variables when they are named; complain about non-capture uses when odr-used. * expr.c (mark_use): Rvalue use looks through capture proxy. * constexpr.c (potential_constant_expression_1): Improve error about use of captured variable. * lambda.c (need_generic_capture, dependent_capture_r) (do_dependent_capture, processing_nonlambda_template): Remove. * call.c (build_this): Remove uses of the above. * decl.c (cp_finish_decl): Likewise. * semantics.c (maybe_cleanup_point_expr) (maybe_cleanup_point_expr_void, finish_goto_stmt) (maybe_convert_cond): Likewise. * typeck.c (check_return_expr): Likewise. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index e18f0770614..e09cf97920b 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -3365,7 +3365,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_nonlambda_template ()) + if (processing_template_decl) return build_address (obj); return cp_build_addr_expr (obj, tf_warning_or_error); diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 670aae22dd6..d6b6843e804 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1286,8 +1286,6 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, && is_dummy_object (x)) { x = ctx->object; - /* We don't use cp_build_addr_expr here because we don't want to - capture the object argument during constexpr evaluation. */ x = build_address (x); } bool lval = false; @@ -5289,7 +5287,25 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, case VAR_DECL: if (DECL_HAS_VALUE_EXPR_P (t)) - return RECUR (DECL_VALUE_EXPR (t), rval); + { + if (now && is_normal_capture_proxy (t)) + { + /* -- in a lambda-expression, a reference to this or to a + variable with automatic storage duration defined outside that + lambda-expression, where the reference would be an + odr-use. */ + if (flags & tf_error) + { + tree cap = DECL_CAPTURED_VARIABLE (t); + error ("lambda capture of %qE is not a constant expression", + cap); + if (!want_rval && decl_constant_var_p (cap)) + inform (input_location, "because it is used as a glvalue"); + } + return false; + } + return RECUR (DECL_VALUE_EXPR (t), rval); + } if (want_rval && !var_in_maybe_constexpr_fn (t) && !type_dependent_expression_p (t) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index ea61e87b2ec..b9942066a07 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6462,7 +6462,6 @@ extern int uses_template_parms (tree); extern bool uses_template_parms_level (tree, int); extern bool in_template_function (void); extern bool need_generic_capture (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, diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 54077d5e331..2e356a0f09a 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -6844,8 +6844,6 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, DECL_INITIAL (decl) = NULL_TREE; } - init = do_dependent_capture (init); - /* Generally, initializers in templates are expanded when the template is instantiated. But, if DECL is a variable constant then it can be used in future constant expressions, so its value diff --git a/gcc/cp/expr.c b/gcc/cp/expr.c index 23e30cf789c..81b9a5b1dc9 100644 --- a/gcc/cp/expr.c +++ b/gcc/cp/expr.c @@ -111,6 +111,14 @@ mark_use (tree expr, bool rvalue_p, bool read_p, { case VAR_DECL: case PARM_DECL: + if (rvalue_p && is_normal_capture_proxy (expr)) + { + /* Look through capture by copy. */ + tree cap = DECL_CAPTURED_VARIABLE (expr); + if (TREE_CODE (TREE_TYPE (cap)) == TREE_CODE (TREE_TYPE (expr)) + && decl_constant_var_p (cap)) + return RECUR (cap); + } if (outer_automatic_var_p (expr) && decl_constant_var_p (expr)) { @@ -146,6 +154,14 @@ mark_use (tree expr, bool rvalue_p, bool read_p, { /* Try to look through the reference. */ tree ref = TREE_OPERAND (expr, 0); + if (rvalue_p && is_normal_capture_proxy (ref)) + { + /* Look through capture by reference. */ + tree cap = DECL_CAPTURED_VARIABLE (ref); + if (TREE_CODE (TREE_TYPE (cap)) != REFERENCE_TYPE + && decl_constant_var_p (cap)) + return RECUR (cap); + } tree r = mark_rvalue_use (ref, loc, reject_builtin); if (r != ref) expr = convert_from_reference (r); diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index 7c8b6409409..2cbad878ff6 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -985,121 +985,6 @@ generic_lambda_fn_p (tree callop) && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (callop))); } -/* Returns true iff we need to consider default capture for an enclosing - generic lambda. */ - -bool -need_generic_capture (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))) - { - tree lam = CLASSTYPE_LAMBDA_EXPR (t); - if (!lam || LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lam) == CPLD_NONE) - /* No default capture. */ - break; - outer_closure = t; - } - - if (!outer_closure) - /* No lambda. */ - return false; - else if (dependent_type_p (outer_closure)) - /* The enclosing context isn't instantiated. */ - return false; - else - return true; -} - -/* A lambda-expression...is said to implicitly capture the entity...if the - compound-statement...names the entity in a potentially-evaluated - expression where the enclosing full-expression depends on a generic lambda - parameter declared within the reaching scope of the lambda-expression. */ - -static tree -dependent_capture_r (tree *tp, int *walk_subtrees, void *data) -{ - hash_set<tree> *pset = (hash_set<tree> *)data; - - if (TYPE_P (*tp)) - *walk_subtrees = 0; - - if (outer_automatic_var_p (*tp)) - { - tree t = process_outer_var_ref (*tp, tf_warning_or_error, /*force*/true); - if (t != *tp - && TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE - && TREE_CODE (TREE_TYPE (*tp)) != REFERENCE_TYPE) - t = convert_from_reference (t); - *tp = t; - } - - if (pset->add (*tp)) - *walk_subtrees = 0; - - switch (TREE_CODE (*tp)) - { - /* Don't walk into unevaluated context or another lambda. */ - case SIZEOF_EXPR: - case ALIGNOF_EXPR: - case TYPEID_EXPR: - case NOEXCEPT_EXPR: - case LAMBDA_EXPR: - *walk_subtrees = 0; - break; - - /* Don't walk into statements whose subexpressions we already - handled. */ - case TRY_BLOCK: - case EH_SPEC_BLOCK: - case HANDLER: - case IF_STMT: - case FOR_STMT: - case RANGE_FOR_STMT: - case WHILE_STMT: - case DO_STMT: - case SWITCH_STMT: - case STATEMENT_LIST: - case RETURN_EXPR: - *walk_subtrees = 0; - break; - - case DECL_EXPR: - { - tree decl = DECL_EXPR_DECL (*tp); - if (VAR_P (decl)) - { - /* walk_tree_1 won't step in here. */ - cp_walk_tree (&DECL_INITIAL (decl), - dependent_capture_r, &pset, NULL); - *walk_subtrees = 0; - } - } - break; - - default: - break; - } - - return NULL_TREE; -} - -tree -do_dependent_capture (tree expr, bool force) -{ - if (!need_generic_capture () - || (!force && !instantiation_dependent_expression_p (expr))) - return expr; - - hash_set<tree> pset; - cp_walk_tree (&expr, dependent_capture_r, &pset, NULL); - return expr; -} - /* If the closure TYPE has a static op(), also add a conversion to function pointer. */ diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 710333ddaba..4ca5974b196 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -9500,16 +9500,6 @@ in_template_function (void) return ret; } -/* Returns true iff we are currently within a template other than a - default-capturing generic lambda, so we don't need to worry about semantic - processing. */ - -bool -processing_nonlambda_template (void) -{ - return processing_template_decl && !need_generic_capture (); -} - /* 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 664952e749c..51489d17ad5 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -410,8 +410,6 @@ maybe_cleanup_point_expr (tree expr) { if (!processing_template_decl && stmts_are_full_exprs_p ()) expr = fold_build_cleanup_point_expr (TREE_TYPE (expr), expr); - else - expr = do_dependent_capture (expr); return expr; } @@ -425,8 +423,6 @@ maybe_cleanup_point_expr_void (tree expr) { if (!processing_template_decl && stmts_are_full_exprs_p ()) expr = fold_build_cleanup_point_expr (void_type_node, expr); - else - expr = do_dependent_capture (expr); return expr; } @@ -633,8 +629,6 @@ finish_goto_stmt (tree destination) = fold_build_cleanup_point_expr (TREE_TYPE (destination), destination); } - else - destination = do_dependent_capture (destination); } check_goto (destination); @@ -656,7 +650,7 @@ maybe_convert_cond (tree cond) /* Wait until we instantiate templates before doing conversion. */ if (processing_template_decl) - return do_dependent_capture (cond); + return cond; if (warn_sequence_point) verify_sequence_points (cond); @@ -3291,10 +3285,14 @@ outer_automatic_var_p (tree decl) } /* DECL satisfies outer_automatic_var_p. Possibly complain about it or - rewrite it for lambda capture. */ + rewrite it for lambda capture. + + If ODR_USE is true, we're being called from mark_use, and we complain about + use of constant variables. If ODR_USE is false, we're being called for the + id-expression, and we do lambda capture. */ tree -process_outer_var_ref (tree decl, tsubst_flags_t complain, bool force_use) +process_outer_var_ref (tree decl, tsubst_flags_t complain, bool odr_use) { if (cp_unevaluated_operand) /* It's not a use (3.2) if we're in an unevaluated context. */ @@ -3315,12 +3313,6 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, bool force_use) 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. */ @@ -3336,7 +3328,7 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, bool force_use) return d; else /* We need to capture an outer proxy. */ - return process_outer_var_ref (d, complain, force_use); + return process_outer_var_ref (d, complain, odr_use); } } @@ -3382,12 +3374,19 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, bool force_use) error ("cannot capture member %qD of anonymous union", decl); return error_mark_node; } - if (context == containing_function) + /* Do lambda capture when processing the id-expression, not when + odr-using a variable. */ + if (!odr_use && context == containing_function) { decl = add_default_capture (lambda_stack, /*id=*/DECL_NAME (decl), initializer); } + /* Only an odr-use of an outer automatic variable causes an + error, and a constant variable can decay to a prvalue + constant without odr-use. So don't complain yet. */ + else if (!odr_use && decl_constant_var_p (decl)) + return decl; else if (lambda_expr) { if (complain & tf_error) diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index f9a5f851463..cb93cc33561 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -9158,7 +9158,7 @@ check_return_expr (tree retval, bool *no_warning) dependent: /* We should not have changed the return value. */ gcc_assert (retval == saved_retval); - return do_dependent_capture (retval, /*force*/true); + return retval; } /* The fabled Named Return Value optimization, as per [class.copy]/15: ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2017-11-13 22:34 UTC | newest] Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2017-09-28 19:39 C++ PATCH for c++/56973 (DR 696), lambda capture of const variables Jason Merrill 2017-10-10 18:07 ` Jason Merrill 2017-11-13 23:14 ` Jason Merrill
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).