From c1051bf7d8c99d056c6ae3353eb2b61751293d2f Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Mon, 13 Nov 2017 17:34:38 -0500 Subject: [PATCH] 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. From-SVN: r254713 --- gcc/cp/ChangeLog | 15 ++++++ gcc/cp/call.c | 2 +- gcc/cp/constexpr.c | 22 +++++++-- gcc/cp/cp-tree.h | 1 - gcc/cp/decl.c | 2 - gcc/cp/expr.c | 16 +++++++ gcc/cp/lambda.c | 115 --------------------------------------------- gcc/cp/pt.c | 10 ---- gcc/cp/semantics.c | 33 +++++++------ gcc/cp/typeck.c | 2 +- 10 files changed, 68 insertions(+), 150 deletions(-) diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index f71506b4ac2..ce0c874a5b0 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,20 @@ 2017-11-13 Jason Merrill + 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. + Defer folding of *&. * typeck.c (cp_build_fold_indirect_ref): New. (cp_build_indirect_ref_1): Split out from cp_build_indirect_ref. 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 *pset = (hash_set *)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 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: -- 2.30.2