On Tue, Oct 10, 2017 at 1:59 PM, Jason Merrill <ja...@redhat.com> wrote:
> On Thu, Sep 28, 2017 at 3:38 PM, Jason Merrill <ja...@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
commit d1730186f54ebc3fa82fe801cd3c500a9b8235af
Author: Jason Merrill <ja...@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:

Reply via email to