This patch allows function parameters to be referenced by trailing
requires clauses. Typically this is used to refer to the type of an
implicitly generated template. For example, the following should now be
valid (where C is some previously defined concept):
auto f1 (auto x) requires C<decltype(x)> ();
Note that the test case trailing-requires-overload.C will fail to
compile unless the previously submitted patch is applied first.
2014-06-17 Braden Obrzut <ad...@maniacsvault.net>
* gcc/cp/parser.c (cp_parser_trailing_requirements): Handle requires
keyword manually so that we can push function parameters back into
scope.
* gcc/cp/decl.c (push_function_parms): New. Recovers and reopens
function parameter scope from declarator.
* gcc/testsuite/g++.dg/concepts/trailing-requires.C: New tests.
* gcc/testsuite/g++.dg/concepts/trailing-requires-overload.C: New
tests.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 5d23bfa..aca3ce5 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5409,6 +5409,7 @@ extern bool defer_mark_used_calls;
extern GTY(()) vec<tree, va_gc> *deferred_mark_used_calls;
extern tree finish_case_label (location_t, tree, tree);
extern tree cxx_maybe_build_cleanup (tree, tsubst_flags_t);
+extern void push_function_parms (cp_declarator *);
/* in decl2.c */
extern bool check_java_method (tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 9791dba..5daccf8 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -13791,6 +13791,22 @@ store_parm_decls (tree current_function_parms)
current_eh_spec_block = begin_eh_spec_block ();
}
+/* Bring the parameters of a function declaration back into scope without
+ entering the function body. Declarator must be a function declarator.
+ Caller is responsible for calling finish_scope. */
+
+void
+push_function_parms (cp_declarator *declarator)
+{
+ begin_scope (sk_function_parms, NULL_TREE);
+
+ for (tree parms = declarator->u.function.parameters; parms != NULL_TREE
+ && !VOID_TYPE_P (TREE_VALUE (parms)); parms = TREE_CHAIN (parms))
+ {
+ pushdecl (TREE_VALUE (parms));
+ }
+}
+
/* We have finished doing semantic analysis on DECL, but have not yet
generated RTL for its body. Save away our current state, so that
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 1eaf863..2d5862f 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -16929,7 +16929,20 @@ cp_parser_trailing_requirements (cp_parser *parser, cp_declarator *decl)
terse_reqs = get_shorthand_requirements (current_template_parms);
// An optional requires clause can yield an additional constraint.
- tree explicit_reqs = cp_parser_requires_clause_opt (parser);
+ tree explicit_reqs = NULL_TREE;
+ if (cp_lexer_next_token_is_keyword (parser->lexer, RID_REQUIRES))
+ {
+ cp_lexer_consume_token (parser->lexer);
+
+ // Bring parms back into scope so requires clause can reference them.
+ ++cp_unevaluated_operand;
+ push_function_parms (decl);
+
+ explicit_reqs = cp_parser_requires_clause (parser);
+
+ finish_scope();
+ --cp_unevaluated_operand;
+ }
// If requirements were specified in either the implicit
// template parameter list or an explicit requires clause,
diff --git a/gcc/testsuite/g++.dg/concepts/trailing-requires-overload.C b/gcc/testsuite/g++.dg/concepts/trailing-requires-overload.C
new file mode 100644
index 0000000..2fc6cdb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/trailing-requires-overload.C
@@ -0,0 +1,115 @@
+// { dg-do run }
+// { dg-options "-std=c++1y" }
+
+#include <cassert>
+
+template<typename T>
+ concept bool C ()
+ {
+ return requires (T a, T b) { { a + b } -> T };
+ }
+
+template<typename T>
+ concept bool D ()
+ {
+ return requires (T a, T b) { { a - b } -> T };
+ }
+
+template<typename T>
+ concept bool M ()
+ {
+ return requires (T a, T b) { { a * b } -> T };
+ }
+
+template<typename T>
+ requires C<T> ()
+ struct Adds
+ {
+ Adds(T a) { v = a; }
+ T v;
+ };
+
+template<typename T>
+ Adds<T> operator+ (const Adds<T> &a, const Adds<T> &b)
+ {
+ return a.v + b.v;
+ }
+
+template<typename T>
+ requires D<T> ()
+ struct Subs
+ {
+ Subs(T a) { v = a; }
+ T v;
+ };
+
+template<typename T>
+ Subs<T> operator- (const Subs<T> &a, const Subs<T> &b)
+ {
+ return a.v - b.v;
+ }
+
+template<typename T>
+ requires M<T> ()
+ struct Mults
+ {
+ Mults(T a) { v = a; }
+ T v;
+ };
+
+template<typename T>
+ Mults<T> operator- (const Mults<T> &a, const Mults<T> &b)
+ {
+ return a.v * b.v;
+ }
+
+auto f1 (auto a, decltype(a) b) -> decltype(a) requires M<decltype(a)> ();
+auto f1 (auto a, decltype(a) b) -> decltype(a) requires D<decltype(a)> ();
+auto f1 (auto a, decltype(a) b) -> decltype(a) requires C<decltype(a)> ();
+
+struct S1
+{
+ auto f2 (auto a) -> decltype(a) requires C<decltype(a)> ();
+ auto f2 (auto a) -> decltype(a) requires D<decltype(a)> ();
+ auto f2 (auto a) -> decltype(a) requires M<decltype(a)> ();
+};
+
+auto S1::f2 (auto a) -> decltype(a) requires M<decltype(a)> ()
+{
+ return f1 (a, a);
+}
+auto S1::f2 (auto a) -> decltype(a) requires C<decltype(a)> ()
+{
+ return f1 (a, a);
+}
+auto S1::f2 (auto a) -> decltype(a) requires D<decltype(a)> ()
+{
+ return f1 (a, a);
+}
+
+auto f1 (auto a, decltype(a) b) -> decltype(a) requires C<decltype(a)> ()
+{
+ return a + b;
+}
+auto f1 (auto a, decltype(a) b) -> decltype(a) requires M<decltype(a)> ()
+{
+ return a * b;
+}
+auto f1 (auto a, decltype(a) b) -> decltype(a) requires D<decltype(a)> ()
+{
+ return a - b;
+}
+
+int main()
+{
+ assert (f1<Adds<int>> (5, 3).v == 8);
+ assert (f1<Subs<int>> (12, 4).v == 8);
+ assert (f1<Mults<int>> (8, 8).v == 64);
+
+ S1 s;
+ assert (s.f2<Adds<int>> (9).v == 18);
+ assert (s.f2<Subs<int>> (9).v == 0);
+ assert (s.f2<Mults<int>> (9).v == 81);
+
+ return 0;
+}
diff --git a/gcc/testsuite/g++.dg/concepts/trailing-requires.C b/gcc/testsuite/g++.dg/concepts/trailing-requires.C
new file mode 100644
index 0000000..4cbaefc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/concepts/trailing-requires.C
@@ -0,0 +1,79 @@
+// { dg-options "-std=c++1y" }
+
+template<typename T>
+ concept bool C ()
+ {
+ return requires (T a, T b) { { a + b } -> T };
+ }
+
+template<typename T>
+ concept bool D ()
+ {
+ return requires (T a, T b) { { a - b } -> T };
+ }
+
+auto f1 (auto a, decltype(a) b) requires C<decltype(a)> ();
+
+auto f2 (auto a, decltype(a) b)
+ requires requires (decltype(a) x, decltype(b) y) { { x + y } -> decltype(a) }
+{
+ return a + b;
+}
+
+auto f1 (auto a, decltype(a) b) requires C<decltype(a)> ()
+{
+ return a + b;
+}
+
+struct S1
+{
+ void f3 (auto a) requires C<decltype(a)> (); // { dg-error "candidate" }
+ auto f4 (auto a) -> decltype(a)
+ requires
+ requires (decltype(a) x, decltype(a) y) { { x + y } -> decltype(a) }
+ {
+ return a;
+ }
+
+ static void f5 (auto a) // { dg-error "candidate" }
+ requires C<decltype(a)> ();
+ static auto f6 (auto a) -> decltype(a)
+ requires
+ requires (decltype(a) x, decltype(a) y) { { x + y } -> decltype(a) }
+ {
+ return a;
+ }
+};
+
+S1 operator+ (const S1 &a, const S1 &b)
+{
+ return S1 ();
+}
+
+void S1::f3 (auto a) {} // { dg-error "match any" }
+void S1::f3 (auto a) requires C<decltype(a)> () {}
+void S1::f5 (auto a) requires D<decltype(a)> () {} // { dg-error "match any" }
+void S1::f5 (auto a) requires C<decltype(a)> () {}
+
+enum { E1, E2 };
+struct S2 {};
+
+int main ()
+{
+ S1 s1, s2;
+
+ f1 (1.2, 2);
+ f2 ('a', 'b');
+ s1.f3(s2);
+ s1.f4(20.f);
+ S1::f5('c');
+ S1::f6(s1);
+
+ f1 (E1, E2); // { dg-error "matching" }
+ f2 (E1, E2); // { dg-error "matching" }
+ s1.f3 (S2 ()); // { dg-error "matching" }
+ s1.f4 (E2); // { dg-error "matching" }
+ S1::f5 (E1); // { dg-error "matching" }
+ S1::f6 (S2 ()); // { dg-error "matching" }
+ return 0;
+}