PR c++/56973, DR 696 - capture constant variables only as needed.
authorJason Merrill <jason@redhat.com>
Thu, 28 Sep 2017 19:39:45 +0000 (15:39 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Thu, 28 Sep 2017 19:39:45 +0000 (15:39 -0400)
* expr.c (mark_use): Split out from mark_rvalue_use and
mark_lvalue_use.  Handle lambda capture of constant variables.
(mark_lvalue_use_nonread): New.
* semantics.c (process_outer_var_ref): Don't capture a constant
variable until forced.
* pt.c (processing_nonlambda_template): New.
* call.c (build_this): Check it.
* decl2.c (grok_array_decl): Call mark_rvalue_use and
mark_lvalue_use_nonread.
* init.c (constant_value_1): Don't call mark_rvalue_use.
* typeck.c (build_static_cast): Handle lambda capture.

From-SVN: r253266

13 files changed:
gcc/cp/ChangeLog
gcc/cp/call.c
gcc/cp/cp-tree.h
gcc/cp/decl2.c
gcc/cp/expr.c
gcc/cp/init.c
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/cp/typeck.c
gcc/testsuite/g++.dg/cpp0x/constexpr-64462.C
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/lambda-generic-const4.C

index 2936f22..6e2e3a8 100644 (file)
@@ -1,5 +1,18 @@
 2017-09-28  Jason Merrill  <jason@redhat.com>
 
+       PR c++/56973, DR 696 - capture constant variables only as needed.
+       * expr.c (mark_use): Split out from mark_rvalue_use and
+       mark_lvalue_use.  Handle lambda capture of constant variables.
+       (mark_lvalue_use_nonread): New.
+       * semantics.c (process_outer_var_ref): Don't capture a constant
+       variable until forced.
+       * pt.c (processing_nonlambda_template): New.
+       * call.c (build_this): Check it.
+       * decl2.c (grok_array_decl): Call mark_rvalue_use and
+       mark_lvalue_use_nonread.
+       * init.c (constant_value_1): Don't call mark_rvalue_use.
+       * typeck.c (build_static_cast): Handle lambda capture.
+
        Use local_specializations to find capture proxies.
        * cp-tree.h (DECL_CAPTURED_VARIABLE): New.
        * lambda.c (build_capture_proxy): Set it.
index 99a7b77..05dc8bb 100644 (file)
@@ -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);
index a634901..f56c951 100644 (file)
@@ -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 *,
index 03e91b7..29d6c59 100644 (file)
@@ -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)
index 8bd341b..f5c8e80 100644 (file)
@@ -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.  */
index b01d662..4bc0755 100644 (file)
@@ -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)
        {
index 2bdac6d..0dae10e 100644 (file)
@@ -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
index 4e87e47..d96423f 100644 (file)
@@ -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))
     {
index 028d56f..326721e 100644 (file)
@@ -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;
     }
 
index 69193fd..8200f87 100644 (file)
@@ -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 (file)
index 0000000..4edfb70
--- /dev/null
@@ -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 (file)
index 0000000..64a37b8
--- /dev/null
@@ -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; };
+}
index 52f4373..d56f379 100644 (file)
@@ -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);
 }