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 <barrettellisad...@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