public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* 2 C++ PATCHes for c++/67411 (const var in generic lambda)
@ 2015-12-20 18:34 Jason Merrill
  2015-12-20 18:51 ` Jakub Jelinek
  0 siblings, 1 reply; 3+ messages in thread
From: Jason Merrill @ 2015-12-20 18:34 UTC (permalink / raw)
  To: gcc-patches List

[-- Attachment #1: Type: text/plain, Size: 1207 bytes --]

Our treatment of references to outer const variables in lambdas has been 
to defer deciding whether or not to capture them until instantiation 
time, at which point their initializers will have been processed.  But 
that doesn't work for generic lambdas, since by the time we instantiate 
the lambda call operator the closure class is already complete, so we 
can't add any captures.

The first patch deals with this specifically for the case of a capture 
proxy (as in the bug report), which is const unless the lambda is 
declared mutable; a capture proxy can never be used in a constant 
expression, so decl_maybe_const_var_p should return false.

The second patch deals with this more generally, for constant variables 
within a generic lambda.  There are two changes: First, within a generic 
lambda we try harder to extract a constant value from a variable by way 
of instantiate_non_dependent_expr.  If that fails, we capture the variable.

Longer term we need to reconsider our model for generic lambdas in 
templates, as really we ought to partially instantiate the lambda body 
when its context is instantiated.

Tested x86_64-pc-linux-gnu, applying to trunk.  First patch applying to 
5 as well.

[-- Attachment #2: 67411-1.patch --]
[-- Type: text/x-patch, Size: 1127 bytes --]

commit 6804dee422ff9a85298a24ae0912e82ed0d7e988
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Dec 17 15:41:32 2015 -0500

    	PR c++/67411
    
    	* decl2.c (decl_maybe_constant_var_p): A proxy isn't constant.

diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 5ae6266..1e4282a 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -4222,6 +4222,9 @@ decl_maybe_constant_var_p (tree decl)
     return false;
   if (DECL_DECLARED_CONSTEXPR_P (decl))
     return true;
+  if (DECL_VALUE_EXPR (decl))
+    /* A proxy isn't constant.  */
+    return false;
   return (CP_TYPE_CONST_NON_VOLATILE_P (type)
 	  && INTEGRAL_OR_ENUMERATION_TYPE_P (type));
 }
diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const1.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const1.C
new file mode 100644
index 0000000..8b54578
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const1.C
@@ -0,0 +1,18 @@
+// PR c++/67411
+// { dg-do compile { target c++14 } }
+
+template <class T>
+void f()
+{
+  int i = 42;
+  [x = i] {
+    [&](auto) {
+      [=] { return x; }();
+    }(1);
+  }();
+}
+
+int main()
+{
+  f<int>();
+}

[-- Attachment #3: 67411-2.patch --]
[-- Type: text/x-patch, Size: 7399 bytes --]

commit 025bb7af15ee0f0468e9b111a144ec3350aed22a
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Dec 18 23:02:11 2015 -0500

    	PR c++/67411
    
    	* lambda.c (generic_lambda_fn_p): Split out from...
    	(maybe_add_lambda_conv_op): ...here.
    	* semantics.c (process_outer_var_ref): Don't defer maybe-constant
    	variables in a generic lambda.
    	* pt.c (instantiate_non_dependent_or_null): New.
    	* init.c (constant_value_1): Use it.
    	* cp-tree.h: Declare it and generic_lambda_fn_p.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 058324f..f0f7e36c 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6158,6 +6158,7 @@ extern bool reregister_specialization		(tree, tree, tree);
 extern tree instantiate_non_dependent_expr	(tree);
 extern tree instantiate_non_dependent_expr_sfinae (tree, tsubst_flags_t);
 extern tree instantiate_non_dependent_expr_internal (tree, tsubst_flags_t);
+extern tree instantiate_non_dependent_or_null   (tree);
 extern bool variable_template_specialization_p  (tree);
 extern bool alias_type_or_template_p            (tree);
 extern bool alias_template_specialization_p     (const_tree);
@@ -6473,6 +6474,7 @@ extern tree maybe_resolve_dummy			(tree, bool);
 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 void maybe_add_lambda_conv_op            (tree);
 extern bool is_lambda_ignored_entity            (tree);
 
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 1d5cc65..09c1183 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -2080,6 +2080,8 @@ constant_value_1 (tree decl, bool strict_p, bool return_aggregate_cst_ok_p)
 	  && TREE_CODE (init) == TREE_LIST
 	  && TREE_CHAIN (init) == NULL_TREE)
 	init = TREE_VALUE (init);
+      /* Instantiate a non-dependent initializer.  */
+      init = instantiate_non_dependent_or_null (init);
       if (!init
 	  || !TREE_TYPE (init)
 	  || !TREE_CONSTANT (init)
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index 8d1ee14..d50e48d 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -851,6 +851,16 @@ prepare_op_call (tree fn, int nargs)
   return t;
 }
 
+/* Return true iff CALLOP is the op() for a generic lambda.  */
+
+bool
+generic_lambda_fn_p (tree callop)
+{
+  return (LAMBDA_FUNCTION_P (callop)
+	  && DECL_TEMPLATE_INFO (callop)
+	  && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (callop)));
+}
+
 /* If the closure TYPE has a static op(), also add a conversion to function
    pointer.  */
 
@@ -867,9 +877,7 @@ maybe_add_lambda_conv_op (tree type)
   if (processing_template_decl)
     return;
 
-  bool const generic_lambda_p
-    = (DECL_TEMPLATE_INFO (callop)
-    && DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (callop)) == callop);
+  bool const generic_lambda_p = generic_lambda_fn_p (callop);
 
   if (!generic_lambda_p && DECL_INITIAL (callop) == NULL_TREE)
     {
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index da57fb2..209e65f 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -5661,6 +5661,28 @@ instantiate_non_dependent_expr (tree expr)
   return instantiate_non_dependent_expr_sfinae (expr, tf_error);
 }
 
+/* Like instantiate_non_dependent_expr, but return NULL_TREE rather than
+   an uninstantiated expression.  */
+
+tree
+instantiate_non_dependent_or_null (tree expr)
+{
+  if (expr == NULL_TREE)
+    return NULL_TREE;
+  if (processing_template_decl)
+    {
+      if (instantiation_dependent_expression_p (expr)
+	  || !potential_constant_expression (expr))
+	expr = NULL_TREE;
+      else
+	{
+	  processing_template_decl_sentinel s;
+	  expr = instantiate_non_dependent_expr_internal (expr, tf_error);
+	}
+    }
+  return expr;
+}
+
 /* True iff T is a specialization of a variable template.  */
 
 bool
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index b8f4e8f..ab9989a 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -3231,27 +3231,7 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain)
   if (!mark_used (decl, complain) && !(complain & tf_error))
     return error_mark_node;
 
-  /* 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_maybe_constant_var_p (decl))
-    {
-      if (processing_template_decl)
-	/* In a template, the constant value may not be in a usable
-	   form, so wait until instantiation time.  */
-	return decl;
-      else if (decl_constant_var_p (decl))
-	{
-	  tree t = maybe_constant_value (convert_from_reference (decl));
-	  if (TREE_CONSTANT (t))
-	    return t;
-	}
-    }
-
+  bool saw_generic_lambda = false;
   if (parsing_nsdmi ())
     containing_function = NULL_TREE;
   else
@@ -3265,6 +3245,9 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain)
 	tree closure = DECL_CONTEXT (containing_function);
 	lambda_expr = CLASSTYPE_LAMBDA_EXPR (closure);
 
+	if (generic_lambda_fn_p (containing_function))
+	  saw_generic_lambda = true;
+
 	if (TYPE_CLASS_SCOPE_P (closure))
 	  /* A lambda in an NSDMI (c++/64496).  */
 	  break;
@@ -3281,6 +3264,35 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain)
 	  = decl_function_context (containing_function);
       }
 
+  /* 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_maybe_constant_var_p (decl))
+    {
+      if (processing_template_decl && !saw_generic_lambda)
+	/* In a non-generic lambda within a template, wait until instantiation
+	   time to decide whether to capture.  For a generic lambda, we can't
+	   wait until we instantiate the op() because the closure class is
+	   already defined at that point.  FIXME to get the semantics exactly
+	   right we need to partially-instantiate the lambda body so the only
+	   dependencies left are on the generic parameters themselves.  This
+	   probably means moving away from our current model of lambdas in
+	   templates (instantiating the closure type) to one based on creating
+	   the closure type when instantiating the lambda context.  That is
+	   probably also the way to handle lambdas within pack expansions.  */
+	return decl;
+      else 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/testsuite/g++.dg/cpp1y/lambda-generic-const2.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const2.C
new file mode 100644
index 0000000..9a00e22
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const2.C
@@ -0,0 +1,19 @@
+// PR c++/67411
+// { dg-do compile { target c++14 } }
+
+template <class T>
+void f()
+{
+  int i = 42;
+  [=] {
+    const int x = i;
+    [&](auto) {
+      [=] { return x; }();
+    }(1);
+  }();
+}
+
+int main()
+{
+  f<int>();
+}

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: 2 C++ PATCHes for c++/67411 (const var in generic lambda)
  2015-12-20 18:34 2 C++ PATCHes for c++/67411 (const var in generic lambda) Jason Merrill
@ 2015-12-20 18:51 ` Jakub Jelinek
  2015-12-21  3:12   ` Jason Merrill
  0 siblings, 1 reply; 3+ messages in thread
From: Jakub Jelinek @ 2015-12-20 18:51 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches List

On Sun, Dec 20, 2015 at 01:34:51PM -0500, Jason Merrill wrote:
> commit 6804dee422ff9a85298a24ae0912e82ed0d7e988
> Author: Jason Merrill <jason@redhat.com>
> Date:   Thu Dec 17 15:41:32 2015 -0500
> 
>     	PR c++/67411
>     
>     	* decl2.c (decl_maybe_constant_var_p): A proxy isn't constant.
> 
> diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
> index 5ae6266..1e4282a 100644
> --- a/gcc/cp/decl2.c
> +++ b/gcc/cp/decl2.c
> @@ -4222,6 +4222,9 @@ decl_maybe_constant_var_p (tree decl)
>      return false;
>    if (DECL_DECLARED_CONSTEXPR_P (decl))
>      return true;
> +  if (DECL_VALUE_EXPR (decl))
> +    /* A proxy isn't constant.  */
> +    return false;

Shouldn't this be DECL_HAS_VALUE_EXPR_P (decl) instead?
Or are you for proxies doing SET_DECL_VALUE_EXPR without setting
DECL_HAS_VALUE_EXPR_P bit?  DECL_HAS_VALUE_EXPR_P is very cheap,
while DECL_VALUE_EXPR is a hash table lookup.

>    return (CP_TYPE_CONST_NON_VOLATILE_P (type)
>  	  && INTEGRAL_OR_ENUMERATION_TYPE_P (type));
>  }

	Jakub

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: 2 C++ PATCHes for c++/67411 (const var in generic lambda)
  2015-12-20 18:51 ` Jakub Jelinek
@ 2015-12-21  3:12   ` Jason Merrill
  0 siblings, 0 replies; 3+ messages in thread
From: Jason Merrill @ 2015-12-21  3:12 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches List

On 12/20/2015 01:51 PM, Jakub Jelinek wrote:
> Shouldn't this be DECL_HAS_VALUE_EXPR_P (decl) instead?

Good point.  Fixed.

Jason


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2015-12-21  3:12 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-12-20 18:34 2 C++ PATCHes for c++/67411 (const var in generic lambda) Jason Merrill
2015-12-20 18:51 ` Jakub Jelinek
2015-12-21  3:12   ` 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).