public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* 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

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).