]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: ICE with enum and conversion fn in template [PR115657]
authorMarek Polacek <polacek@redhat.com>
Thu, 15 Aug 2024 22:47:29 +0000 (18:47 -0400)
committerMarek Polacek <polacek@redhat.com>
Mon, 19 Aug 2024 17:29:30 +0000 (13:29 -0400)
Here we initialize an enumerator with a class prvalue with a conversion
function.  When we fold it in build_enumerator, we create a TARGET_EXPR
for the object, and subsequently crash in tsubst_expr, which should not
see such a code.

Normally, we fix similar problems by using an IMPLICIT_CONV_EXPR but here
I may get away with not using the result of fold_non_dependent_expr unless
the result is a constant.  A TARGET_EXPR is not constant.

PR c++/115657

gcc/cp/ChangeLog:

* decl.cc (build_enumerator): Call maybe_fold_non_dependent_expr
instead of fold_non_dependent_expr.

gcc/testsuite/ChangeLog:

* g++.dg/cpp1y/constexpr-recursion2.C: New test.
* g++.dg/template/conv21.C: New test.

gcc/cp/decl.cc
gcc/testsuite/g++.dg/cpp1y/constexpr-recursion2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/conv21.C [new file with mode: 0644]

index f23b635aec9f70a3a9c7eafa89875beca58fb7ba..12139e1d86279ca25f6e9e001b752fc705de7021 100644 (file)
@@ -17387,9 +17387,15 @@ build_enumerator (tree name, tree value, tree enumtype, tree attributes,
   tree type;
 
   /* scalar_constant_value will pull out this expression, so make sure
-     it's folded as appropriate.  */
+     it's folded as appropriate.
+
+     Creating a TARGET_EXPR in a template breaks when substituting, and
+     here we would create it for instance when using a class prvalue with
+     a user-defined conversion function.  So don't use such a tree.  We
+     instantiate VALUE here to get errors about bad enumerators even in
+     a template that does not get instantiated.  */
   if (processing_template_decl)
-    value = fold_non_dependent_expr (value);
+    value = maybe_fold_non_dependent_expr (value);
 
   /* If the VALUE was erroneous, pretend it wasn't there; that will
      result in the enum being assigned the next value in sequence.  */
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-recursion2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-recursion2.C
new file mode 100644 (file)
index 0000000..f268f52
--- /dev/null
@@ -0,0 +1,22 @@
+// PR c++/115657
+// { dg-do compile { target c++14 } }
+// { dg-options "-Wall" }
+
+// Like constexpr-recursion1.C but use a class with a conversion function.
+
+struct X {
+  constexpr operator int() { return 0; }
+};
+
+template <int N>
+constexpr X f1 ()
+{
+  enum E { a = f1<0> () }; // { dg-error "called in a constant expression before its definition is complete|is not an integer constant" }
+  return {};
+}
+
+constexpr X f3 ()
+{
+  enum E { a = f3 () };        // { dg-error "called in a constant expression before its definition is complete|is not an integer constant" }
+  return {};
+}
diff --git a/gcc/testsuite/g++.dg/template/conv21.C b/gcc/testsuite/g++.dg/template/conv21.C
new file mode 100644 (file)
index 0000000..1dc7b3d
--- /dev/null
@@ -0,0 +1,14 @@
+// PR c++/115657
+// { dg-do compile { target c++11 } }
+
+struct NonIntegral
+{
+    constexpr operator int() { return 0; }
+};
+
+template<typename T> struct TemplatedStructural
+{
+    enum { e = NonIntegral{} };
+};
+
+template struct TemplatedStructural<void>;