diff mbox series

C++ PATCH for c++/85200, ICE with constexpr if in generic lambda

Message ID CADzB+2kYHVOiSr4zbmOkJVbi5DCqfpwTpQoVf_04Uc2HxTqiow@mail.gmail.com
State New
Headers show
Series C++ PATCH for c++/85200, ICE with constexpr if in generic lambda | expand

Commit Message

Jason Merrill April 4, 2018, 6:57 p.m. UTC
Since, during partial instantiation of a generic lambda, we don't
substitute into the body of a constexpr if, we don't do lambda capture
as part of substitution.  But extract_locals_r is supposed to do it as
part of remembering the substitution context to be used later.

As it turns out, this was failing because walk_tree_1 expects the
DECL_INITIAL of a local variable to be walked in the context of the
BIND_EXPR, but we don't build BIND_EXPRs for compound-statements in a
template.  So in a template, let's walk the fields of a VAR_DECL from
its DECL_EXPR.

Tested x86_64-pc-linux-gnu, applying to trunk.

Comments

Jason Merrill April 5, 2018, 2:18 p.m. UTC | #1
On Wed, Apr 4, 2018 at 2:57 PM, Jason Merrill <jason@redhat.com> wrote:
> Since, during partial instantiation of a generic lambda, we don't
> substitute into the body of a constexpr if, we don't do lambda capture
> as part of substitution.  But extract_locals_r is supposed to do it as
> part of remembering the substitution context to be used later.
>
> As it turns out, this was failing because walk_tree_1 expects the
> DECL_INITIAL of a local variable to be walked in the context of the
> BIND_EXPR, but we don't build BIND_EXPRs for compound-statements in a
> template.  So in a template, let's walk the fields of a VAR_DECL from
> its DECL_EXPR.

...but I forgot to also check the original testcase, which was also
failing for a different reason: we were remembering the mapping from
fully general to partially instantiated even for the condition
variable of the constexpr if, which is wrong because we are discarding
that instantiation at this point, so using it in a later instantiation
breaks.

I also considered specifically removing the condition variable from
local_instantiations, but this more general approach seems safer.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit a4b63e3bd89af40e316b1f16f0bba9001cf8b351
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Apr 5 09:12:05 2018 -0400

            PR c++/85200 - ICE with constexpr if in generic lambda.
    
            * pt.c (extract_locals_r): Don't record the local specs of variables
            declared within the pattern.

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 741c578b65b..204e390353c 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -11597,8 +11597,12 @@ tsubst_binary_right_fold (tree t, tree args, tsubst_flags_t complain,
 
 struct el_data
 {
+  hash_set<tree> internal;
   tree extra;
   tsubst_flags_t complain;
+
+  el_data (tsubst_flags_t c)
+    : extra(NULL_TREE), complain(c) {}
 };
 static tree
 extract_locals_r (tree *tp, int */*walk_subtrees*/, void *data_)
@@ -11606,8 +11610,13 @@ extract_locals_r (tree *tp, int */*walk_subtrees*/, void *data_)
   el_data &data = *reinterpret_cast<el_data*>(data_);
   tree *extra = &data.extra;
   tsubst_flags_t complain = data.complain;
-  if (tree spec = retrieve_local_specialization (*tp))
+  if (TREE_CODE (*tp) == DECL_EXPR)
+    data.internal.add (DECL_EXPR_DECL (*tp));
+  else if (tree spec = retrieve_local_specialization (*tp))
     {
+      if (data.internal.contains (*tp))
+	/* Don't mess with variables declared within the pattern.  */
+	return NULL_TREE;
       if (TREE_CODE (spec) == NONTYPE_ARGUMENT_PACK)
 	{
 	  /* Maybe pull out the PARM_DECL for a partial instantiation.  */
@@ -11658,7 +11667,7 @@ extract_locals_r (tree *tp, int */*walk_subtrees*/, void *data_)
 static tree
 extract_local_specs (tree pattern, tsubst_flags_t complain)
 {
-  el_data data = { NULL_TREE, complain };
+  el_data data (complain);
   cp_walk_tree_without_duplicates (&pattern, extract_locals_r, &data);
   return data.extra;
 }
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-if19.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-if19.C
new file mode 100644
index 00000000000..40016a5b7e1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-if19.C
@@ -0,0 +1,17 @@
+// PR c++/85200
+// { dg-additional-options -std=c++17 }
+
+struct A{
+    constexpr operator int(){ return 0; }
+};
+
+template < typename >
+void f(){
+    [](auto known){
+        if constexpr(constexpr int k = known);
+    }(A{});
+}
+
+int main(){
+    f<int>();
+}
diff mbox series

Patch

commit a99a47e6326b7278d660dcebba8ebcbec863f04a
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Apr 4 14:24:34 2018 -0400

            PR c++/85200 - ICE with constexpr if in generic lambda.
    
            * tree.c (cp_walk_subtrees): Walk into DECL_EXPR in templates.

diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 7ddc2cb5e2d..d0835cfaa29 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -4894,10 +4894,12 @@  cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func,
       /* User variables should be mentioned in BIND_EXPR_VARS
 	 and their initializers and sizes walked when walking
 	 the containing BIND_EXPR.  Compiler temporaries are
-	 handled here.  */
+	 handled here.  And also normal variables in templates,
+	 since do_poplevel doesn't build a BIND_EXPR then.  */
       if (VAR_P (TREE_OPERAND (*tp, 0))
-	  && DECL_ARTIFICIAL (TREE_OPERAND (*tp, 0))
-	  && !TREE_STATIC (TREE_OPERAND (*tp, 0)))
+	  && (processing_template_decl
+	      || (DECL_ARTIFICIAL (TREE_OPERAND (*tp, 0))
+		  && !TREE_STATIC (TREE_OPERAND (*tp, 0)))))
 	{
 	  tree decl = TREE_OPERAND (*tp, 0);
 	  WALK_SUBTREE (DECL_INITIAL (decl));
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-if18.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-if18.C
new file mode 100644
index 00000000000..03ad620e8d9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-if18.C
@@ -0,0 +1,15 @@ 
+// PR c++/85200
+// { dg-additional-options -std=c++17 }
+
+template <typename T>
+void f(){
+  [](auto v, auto b){
+    if constexpr (sizeof(v) == sizeof(int)) {
+	auto x = b;
+      }
+  }(0, 1);
+}
+
+int main(){
+  f<int>();
+}