[RFC] c++: Allow parm use outside function body for constexpr members

Message ID CAARDWz94_=xSzHuVmpqp-8F-fWoZN+jog390=fKNpd6fwRnzyg@mail.gmail.com
State Changes Requested
Headers
Series [RFC] c++: Allow parm use outside function body for constexpr members |

Commit Message

Barrett Adair Sept. 17, 2021, 4:15 p.m. UTC
  The WIP attached patch attempts to enable usage of parameters' constexpr
members before the function body begins (see new tests dependent-expr11.C
and dependent-expr12.C).

Unfortunately, I hit a "vexing" snag: gcc/testsuite/g++.dg/parse/ambig7.C
breaks with this patch. After several hours of debugging, here's what I
think happens:

In trunk, ambig7.C's testFail line compiles because the tentative parse --
where "testFail" is a function and int(A) is a parameter type -- fails only
when seeing that function<A> uses "parameter outside function body" (the
error removed by this patch). This occurs when parsing the template
argument list, before calling finish_template_type.

After applying the patch, the tentative parse still fails because
function<A> is not a type, but this only occurs after finish_template_type
is called, which causes the incorrect template-id for function<A> to be
cached with broken arguments, such that function<A> remains "unresolved
overloaded function type":

ambig7.C:16:36: error: no matching function for call to
‘Helper::Helper(int, <unresolved overloaded function type>)’
   16 | Helper testFail(int(A), function<A>);

Any suggestions on how to go about fixing this? For instance, is it
possible to repair or roll back the cached template instantation?
  

Comments

Barrett Adair Sept. 23, 2021, 5:59 p.m. UTC | #1
I'm past this snag, so disregard this RFC for now. If I post again about
this, it will be with a working patch.

On Fri, Sep 17, 2021 at 11:15 AM Barrett Adair <barrettellisadair@gmail.com>
wrote:

> The WIP attached patch attempts to enable usage of parameters' constexpr
> members before the function body begins (see new tests dependent-expr11.C
> and dependent-expr12.C).
>
> Unfortunately, I hit a "vexing" snag: gcc/testsuite/g++.dg/parse/ambig7.C
> breaks with this patch. After several hours of debugging, here's what I
> think happens:
>
> In trunk, ambig7.C's testFail line compiles because the tentative parse --
> where "testFail" is a function and int(A) is a parameter type -- fails only
> when seeing that function<A> uses "parameter outside function body" (the
> error removed by this patch). This occurs when parsing the template
> argument list, before calling finish_template_type.
>
> After applying the patch, the tentative parse still fails because
> function<A> is not a type, but this only occurs after finish_template_type
> is called, which causes the incorrect template-id for function<A> to be
> cached with broken arguments, such that function<A> remains "unresolved
> overloaded function type":
>
> ambig7.C:16:36: error: no matching function for call to
> ‘Helper::Helper(int, <unresolved overloaded function type>)’
>    16 | Helper testFail(int(A), function<A>);
>
> Any suggestions on how to go about fixing this? For instance, is it
> possible to repair or roll back the cached template instantation?
>
>
  

Patch

From 609cbe53b758afaf344f6798be776cce0d00a3fa Mon Sep 17 00:00:00 2001
From: Barrett Adair <barrettellisadair@gmail.com>
Date: Fri, 17 Sep 2021 10:46:46 -0500
Subject: [PATCH] Allow parm use outside function body for constexpr members

---
 gcc/cp/pt.c                                         | 11 +++++++----
 gcc/cp/semantics.c                                  | 13 ++-----------
 gcc/testsuite/g++.dg/cpp0x/noexcept34.C             |  2 +-
 .../g++.dg/parse/parameter-declaration-2.C          |  2 +-
 gcc/testsuite/g++.dg/template/dependent-expr11.C    |  6 ++++++
 gcc/testsuite/g++.dg/template/dependent-expr12.C    | 10 ++++++++++
 gcc/testsuite/g++.dg/template/memfriend19.C         | 12 ++++++++++++
 gcc/testsuite/g++.dg/template/memfriend20.C         | 11 +++++++++++
 8 files changed, 50 insertions(+), 17 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-expr11.C
 create mode 100644 gcc/testsuite/g++.dg/template/dependent-expr12.C
 create mode 100644 gcc/testsuite/g++.dg/template/memfriend19.C
 create mode 100644 gcc/testsuite/g++.dg/template/memfriend20.C

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 12c8812d8b2..3e864c9fd8e 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -15133,9 +15133,13 @@  tsubst_function_type (tree t,
 	  inject_this_parameter (this_type, cp_type_quals (this_type));
 	}
 
+      begin_scope (sk_function_parms, in_decl);
+
       /* Substitute the return type.  */
       return_type = tsubst (TREE_TYPE (t), args, complain, in_decl);
 
+      finish_scope ();
+
       if (do_inject)
 	{
 	  current_class_ptr = save_ccp;
@@ -16594,10 +16598,9 @@  tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	  if (DECL_NAME (t) == this_identifier && current_class_ptr)
 	    return current_class_ptr;
 
-	  /* This can happen for a parameter name used later in a function
-	     declaration (such as in a late-specified return type).  Just
-	     make a dummy decl, since it's only used for its type.  */
-	  gcc_assert (cp_unevaluated_operand != 0);
+	  gcc_assert (cp_unevaluated_operand != 0
+	      || current_binding_level->kind == sk_function_parms);
+
 	  r = tsubst_decl (t, args, complain);
 	  /* Give it the template pattern as its context; its true context
 	     hasn't been instantiated yet and this is good enough for
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 35a7b9f7b83..d0987166361 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -3742,7 +3742,8 @@  outer_var_p (tree decl)
 	  && DECL_FUNCTION_SCOPE_P (decl)
 	  /* Don't get confused by temporaries.  */
 	  && DECL_NAME (decl)
-	  && (DECL_CONTEXT (decl) != current_function_decl
+	  && ((current_function_decl != NULL
+	        && DECL_CONTEXT (decl) != current_function_decl)
 	      || parsing_nsdmi ()));
 }
 
@@ -4002,16 +4003,6 @@  finish_id_expression_1 (tree id_expression,
 	  if (decl == error_mark_node)
 	    return error_mark_node;
 	}
-
-      /* Also disallow uses of function parameters outside the function
-	 body, except inside an unevaluated context (i.e. decltype).  */
-      if (TREE_CODE (decl) == PARM_DECL
-	  && DECL_CONTEXT (decl) == NULL_TREE
-	  && !cp_unevaluated_operand)
-	{
-	  *error_msg = G_("use of parameter outside function body");
-	  return error_mark_node;
-	}
     }
 
   /* If we didn't find anything, or what we found was a type,
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept34.C b/gcc/testsuite/g++.dg/cpp0x/noexcept34.C
index dce35652ef5..822a8597f63 100644
--- a/gcc/testsuite/g++.dg/cpp0x/noexcept34.C
+++ b/gcc/testsuite/g++.dg/cpp0x/noexcept34.C
@@ -10,7 +10,7 @@  template<typename> struct A
   void g () noexcept (f()) { } // { dg-error "use of parameter" }
   void g2 () noexcept (this->f()) { } // { dg-error "use of parameter" }
   void g3 () noexcept (b) { } // { dg-error "use of .this. in a constant expression|use of parameter" }
-  void g4 (int i) noexcept (i) { } // { dg-error "use of parameter" }
+  void g4 (int i) noexcept (i) { } // { dg-error "use of parameter|not a constant expression" }
   void g5 () noexcept (A::f()) { } // { dg-error "use of parameter" }
   void g6 () noexcept (foo(b)) { } // { dg-error "use of .this. in a constant expression|use of parameter" }
   void g7 () noexcept (int{f()}) { } // { dg-error "use of parameter" }
diff --git a/gcc/testsuite/g++.dg/parse/parameter-declaration-2.C b/gcc/testsuite/g++.dg/parse/parameter-declaration-2.C
index 3c983cc748c..236cda7e3be 100644
--- a/gcc/testsuite/g++.dg/parse/parameter-declaration-2.C
+++ b/gcc/testsuite/g++.dg/parse/parameter-declaration-2.C
@@ -1,2 +1,2 @@ 
-void f (int i, int p[i]); // { dg-error "use of parameter.*outside function body" }
+void f (int i, int p[i]); // { dg-error "use of parameter.*outside function body|integral constant" }
 // { dg-prune-output "array bound" }
diff --git a/gcc/testsuite/g++.dg/template/dependent-expr11.C b/gcc/testsuite/g++.dg/template/dependent-expr11.C
new file mode 100644
index 00000000000..d1fa63fc792
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-expr11.C
@@ -0,0 +1,6 @@ 
+// { dg-do compile { target c++11 } }
+template<bool u> struct bool_c{ static constexpr bool value = u; };
+struct foo { constexpr bool operator()() const { return true; } };
+template<class T> auto bar(T t) -> bool_c<t()>;
+template<class T> auto bar(T t) -> bool_c<t()>;
+static_assert(decltype(bar(foo{}))::value, "");
diff --git a/gcc/testsuite/g++.dg/template/dependent-expr12.C b/gcc/testsuite/g++.dg/template/dependent-expr12.C
new file mode 100644
index 00000000000..1ebc132f7d9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/dependent-expr12.C
@@ -0,0 +1,10 @@ 
+// { dg-do compile { target c++14 } }
+template<int i>
+struct foo {
+  constexpr operator int() { return i; } 
+};
+void bar() {
+  [](auto i) -> foo<i> {
+    return {};
+  };
+}
diff --git a/gcc/testsuite/g++.dg/template/memfriend19.C b/gcc/testsuite/g++.dg/template/memfriend19.C
new file mode 100644
index 00000000000..b33d11dc10d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/memfriend19.C
@@ -0,0 +1,12 @@ 
+// { dg-do compile { target c++11 } }
+template <typename T>
+struct foo {
+  auto operator()(T t) -> decltype(t->v);
+};
+
+class bar {
+  friend struct foo<bar*>;
+  int v;
+};
+
+decltype(foo<bar*>{}(nullptr)) x;
diff --git a/gcc/testsuite/g++.dg/template/memfriend20.C b/gcc/testsuite/g++.dg/template/memfriend20.C
new file mode 100644
index 00000000000..42373abacc7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/memfriend20.C
@@ -0,0 +1,11 @@ 
+// { dg-do compile { target c++11 } }
+template <typename T>
+struct foo {
+  auto operator()(T t) -> decltype(t->v); // { dg-error "private" }
+};
+
+class bar {
+  int v;
+};
+
+decltype(foo<bar*>{}(nullptr)) x;
-- 
2.30.2