]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: recursive template with deduced return [PR120555]
authorJason Merrill <jason@redhat.com>
Fri, 6 Jun 2025 14:26:28 +0000 (10:26 -0400)
committerJason Merrill <jason@redhat.com>
Fri, 6 Jun 2025 19:43:47 +0000 (15:43 -0400)
Here since r15-4120 we were prematurely complaining about the use of func
within its own definiton, which is fine at instantiation time.  So don't
require this for function templates that are currently being defined.

But keep the error for instantiations of templates that are not currently
being defined, which we similarly did not diagnose before r15-4120 but other
implementations do.

Both of these follow the general principle from [temp.res.general]/6 that we
only error in a template body if no instatiation could be well-formed.

Also remove a redundant call to require_deduced_type.

PR c++/120555

gcc/cp/ChangeLog:

* decl2.cc (fn_being_defined, fn_template_being_defined): New.
(mark_used): Check fn_template_being_defined.

gcc/testsuite/ChangeLog:

* g++.dg/cpp1z/constexpr-if39.C: New test.

gcc/cp/decl2.cc
gcc/testsuite/g++.dg/cpp1z/constexpr-if39.C [new file with mode: 0644]

index e3fbc409392907876800301d995c03fb1de293cb..2bbc6180b0b9f33a4afd72e2dd427de7fc38a82d 100644 (file)
@@ -6269,6 +6269,33 @@ mark_single_function (tree expr, tsubst_flags_t complain)
   return true;
 }
 
+/* True iff we have started, but not finished, defining FUNCTION_DECL DECL.  */
+
+bool
+fn_being_defined (tree decl)
+{
+  /* DECL_INITIAL is set to error_mark_node in grokfndecl for a definition, and
+     changed to BLOCK by poplevel at the end of the function.  */
+  return (TREE_CODE (decl) == FUNCTION_DECL
+         && DECL_INITIAL (decl) == error_mark_node);
+}
+
+/* True if DECL is an instantiation of a function template currently being
+   defined.  */
+
+bool
+fn_template_being_defined (tree decl)
+{
+  if (TREE_CODE (decl) != FUNCTION_DECL
+      || !DECL_LANG_SPECIFIC (decl)
+      || !DECL_TEMPLOID_INSTANTIATION (decl)
+      || DECL_TEMPLATE_INSTANTIATED (decl))
+    return false;
+  tree tinfo = DECL_TEMPLATE_INFO (decl);
+  tree pattern = DECL_TEMPLATE_RESULT (TI_TEMPLATE (tinfo));
+  return fn_being_defined (pattern);
+}
+
 /* Mark DECL (either a _DECL or a BASELINK) as "used" in the program.
    If DECL is a specialization or implicitly declared class member,
    generate the actual definition.  Return false if something goes
@@ -6422,6 +6449,9 @@ mark_used (tree decl, tsubst_flags_t complain /* = tf_warning_or_error */)
     maybe_instantiate_decl (decl);
 
   if (!decl_dependent_p (decl)
+      /* Don't require this yet for an instantiation of a function template
+        we're currently defining (c++/120555).  */
+      && !fn_template_being_defined (decl)
       && !require_deduced_type (decl, complain))
     return false;
 
@@ -6436,9 +6466,6 @@ mark_used (tree decl, tsubst_flags_t complain /* = tf_warning_or_error */)
       && uses_template_parms (DECL_TI_ARGS (decl)))
     return true;
 
-  if (!require_deduced_type (decl, complain))
-    return false;
-
   if (builtin_pack_fn_p (decl))
     {
       error ("use of built-in parameter pack %qD outside of a template",
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-if39.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-if39.C
new file mode 100644 (file)
index 0000000..38ae7a0
--- /dev/null
@@ -0,0 +1,30 @@
+// PR c++/120555
+// { dg-do compile { target c++17 } }
+
+struct A { int m; };
+
+template<class T>
+constexpr auto f() {
+  if constexpr (sizeof(T) == sizeof(int))
+    return 1;
+  else
+    return A{f<int>()};
+}
+
+static_assert(f<bool>().m == 1);
+static_assert(f<int>() == 1);
+
+template <class T> constexpr auto g();
+
+template<class T>
+constexpr auto f2() {
+  if constexpr (sizeof(T) == sizeof(int))
+    return 1;
+  else
+    return A{g<int>()};                // { dg-error "auto" }
+}
+
+template <class T> constexpr auto g() { return A{1}; }
+
+static_assert(f2<bool>().m == 1);
+static_assert(f2<int>() == 1);