[RFC] c++: Allow parm use outside function body for constexpr members
Commit Message
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
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?
>
>
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
@@ -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
@@ -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,
@@ -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" }
@@ -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" }
new file mode 100644
@@ -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, "");
new file mode 100644
@@ -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 {};
+ };
+}
new file mode 100644
@@ -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;
new file mode 100644
@@ -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