]> 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>
Mon, 9 Jun 2025 13:22:07 +0000 (09:22 -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.

(cherry picked from commit 8d204f2a536f7253e4251aca7bc12af524800b4c)

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

index bceaf78b8c45e22ce8fad997d20de76c26d135b0..63a5a21511df2a12b1635a18d2034eb11368f215 100644 (file)
@@ -6267,6 +6267,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
@@ -6415,6 +6442,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;
 
@@ -6429,9 +6459,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);