public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [c++-concepts] Allow function parameters to be referenced in trailing requires clauses
@ 2014-06-17  9:06 Braden Obrzut
  2014-07-30 11:55 ` Andrew Sutton
  0 siblings, 1 reply; 2+ messages in thread
From: Braden Obrzut @ 2014-06-17  9:06 UTC (permalink / raw)
  To: gcc-patches; +Cc: Andrew Sutton

[-- Attachment #1: Type: text/plain, Size: 918 bytes --]

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

[-- Attachment #2: trailing-requires.diff --]
[-- Type: text/x-patch, Size: 7088 bytes --]

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));
+    }
+}
+
 \f
 /* 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;
+}

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [c++-concepts] Allow function parameters to be referenced in trailing requires clauses
  2014-06-17  9:06 [c++-concepts] Allow function parameters to be referenced in trailing requires clauses Braden Obrzut
@ 2014-07-30 11:55 ` Andrew Sutton
  0 siblings, 0 replies; 2+ messages in thread
From: Andrew Sutton @ 2014-07-30 11:55 UTC (permalink / raw)
  To: gcc-patches, Braden Obrzut, Jason Merrill

[-- Attachment #1: Type: text/plain, Size: 1591 bytes --]

Applied an updated version of this patch.

2014-7-30  Braden Obrzut  <admin@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/req*.C: New tests.

2014-07-30  Andrew Sutton  <andrew.n.sutton@gmail.com>
        * gcc/testsuite/g++.dg/concepts/test.C: Removed.

Andrew Sutton


On Tue, Jun 17, 2014 at 5:06 AM, Braden Obrzut <admin@maniacsvault.net> wrote:
> 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  <admin@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.

[-- Attachment #2: trailing-requires-4-test.patch --]
[-- Type: text/x-patch, Size: 4022 bytes --]

Index: gcc/testsuite/g++.dg/concepts/traits1.C
===================================================================
--- gcc/testsuite/g++.dg/concepts/traits1.C	(revision 212456)
+++ gcc/testsuite/g++.dg/concepts/traits1.C	(working copy)
@@ -79,21 +79,21 @@ void f18() requires Enum<void>();
 
 
 int main() { 
-  f1(); // { dg-error "cannot" }
-  f2(); // { dg-error "cannot" }
-  f3(); // { dg-error "cannot" }
-  f4(); // { dg-error "cannot" }
-  f5(); // { dg-error "cannot" }
-  f6(); // { dg-error "cannot" }
-  f7(); // { dg-error "cannot" }
-  f8(); // { dg-error "cannot" }
-  f9(); // { dg-error "cannot" }
-  f10(); // { dg-error "cannot" }
-  f11(); // { dg-error "cannot" }
-  f12(); // { dg-error "cannot" }
-  f13(); // { dg-error "cannot" }
-  f14(); // { dg-error "cannot" }
-  f15(); // { dg-error "cannot" }
-  f16(); // { dg-error "cannot" }
-  f17(); // { dg-error "cannot" }
+  f1(); // { dg-error "cannot call" }
+  f2(); // { dg-error "cannot call" }
+  f3(); // { dg-error "cannot call" }
+  f4(); // { dg-error "cannot call" }
+  f5(); // { dg-error "cannot call" }
+  f6(); // { dg-error "cannot call" }
+  f7(); // { dg-error "cannot call" }
+  f8(); // { dg-error "cannot call" }
+  f9(); // { dg-error "cannot call" }
+  f10(); // { dg-error "cannot call" }
+  f11(); // { dg-error "cannot call" }
+  f12(); // { dg-error "cannot call" }
+  f13(); // { dg-error "cannot call" }
+  f14(); // { dg-error "cannot call" }
+  f15(); // { dg-error "cannot call" }
+  f16(); // { dg-error "cannot call" }
+  f17(); // { dg-error "cannot call" }
 }
Index: gcc/testsuite/g++.dg/concepts/req1.C
===================================================================
--- gcc/testsuite/g++.dg/concepts/req1.C	(revision 0)
+++ gcc/testsuite/g++.dg/concepts/req1.C	(revision 0)
@@ -0,0 +1,15 @@
+// { dg-do run }
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+  concept bool Class () { return __is_class(T); }
+
+void f1(auto a) requires Class<decltype(a)>() { }
+void f2(auto a) requires requires (decltype(a) x) { -x; } { }
+
+struct S { } s;
+
+int main() {
+  f1(s);
+  f2(0);
+}
Index: gcc/testsuite/g++.dg/concepts/req2.C
===================================================================
--- gcc/testsuite/g++.dg/concepts/req2.C	(revision 0)
+++ gcc/testsuite/g++.dg/concepts/req2.C	(revision 0)
@@ -0,0 +1,21 @@
+// { dg-options "-std=c++1z" }
+
+#include <cassert>
+
+template<typename T>
+  concept bool Class () { return __is_class(T); }
+
+void f1 (auto a) requires Class<decltype(a)>() { }
+
+  // FIXME: This is generating excess errors related to pretty
+  // printing the trailing requires expression.
+void f2(auto a) 
+  requires requires (decltype(a) x) { -x; } 
+{ } 
+
+struct S { } s;
+
+int main() {
+  f1(0); // { dg-error "matching" }
+  f2((void*)0); // { dg-error "matching" }
+}
Index: gcc/testsuite/g++.dg/concepts/req3.C
===================================================================
--- gcc/testsuite/g++.dg/concepts/req3.C	(revision 0)
+++ gcc/testsuite/g++.dg/concepts/req3.C	(revision 0)
@@ -0,0 +1,18 @@
+// { dg-do run }
+// { dg-options "-std=c++1z" }
+
+template<typename T>
+  concept bool Class () { return __is_class(T); }
+
+struct Test {
+  void f(auto a) requires Class<decltype(a)>();
+} test;
+
+struct S { }s;
+
+int main() {
+  test.f(s);
+}
+
+void Test::f(auto a) requires Class<decltype(a)>() { }
+
Index: gcc/testsuite/g++.dg/concepts/test.C
===================================================================
--- gcc/testsuite/g++.dg/concepts/test.C	(revision 212100)
+++ gcc/testsuite/g++.dg/concepts/test.C	(working copy)
@@ -1,17 +0,0 @@
-// { dg-options "-I/home/faculty/asutton/Code/origin -std=c++1z" }
-
-#include <algorithm>
-
-// #include <origin/range/stream.hpp>
-
-// int main() {
-//   std::string s = "1 2 3 4 5";
-//   std::istringstream ss(s);
-//   auto is = origin::make_istream<int>(ss);
-
-//   for(int x : is) {
-//      std::cout << x << '\n';
-//   }
-
-//   static_assert(not origin::Range<decltype(is)>(), "");
-// }

[-- Attachment #3: trailing-requires-4.patch --]
[-- Type: text/x-patch, Size: 2731 bytes --]

Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 212456)
+++ gcc/cp/parser.c	(working copy)
@@ -16982,14 +16982,22 @@ struct cp_manage_requirements {
 static tree
 cp_parser_trailing_requirements (cp_parser *parser, cp_declarator *decl) 
 {
-  // A function declaration may have a trailing requires-clause.
   if (function_declarator_p (decl))
-    if (tree reqs = cp_parser_requires_clause_opt (parser))
-      current_template_reqs = save_trailing_requirements (reqs);
+    {
+      if (cp_lexer_next_token_is_keyword (parser->lexer, RID_REQUIRES))
+        {
+          ++cp_unevaluated_operand;
+          push_function_parms (decl);
+          cp_lexer_consume_token (parser->lexer);
+          tree reqs = cp_parser_requires_clause (parser);
+          current_template_reqs = save_trailing_requirements (reqs);
+          finish_scope();
+          --cp_unevaluated_operand;
+        }
+    }
   return current_template_reqs;
 }
 
-
 /* Declarators [gram.dcl.decl] */
 
 /* Parse an init-declarator.
Index: gcc/cp/decl.c
===================================================================
--- gcc/cp/decl.c	(revision 212456)
+++ gcc/cp/decl.c	(working copy)
@@ -13864,6 +13864,30 @@ store_parm_decls (tree current_function_
     current_eh_spec_block = begin_eh_spec_block ();
 }
 
+// Bring the parameters of a function declaration back into 
+// scope without entering the function body. The declarator 
+// must be a function declarator. The caller is responsible 
+// for calling finish_scope.
+void
+push_function_parms (cp_declarator *declarator)
+{
+  // Find the actual function declarator.
+  while (declarator)
+    {
+      if (declarator->kind == cdk_function)
+        break;
+      declarator = declarator->declarator;
+    }
+
+  begin_scope (sk_function_parms, NULL_TREE);
+  tree p = declarator->u.function.parameters;
+  while (p != NULL_TREE && !VOID_TYPE_P (TREE_VALUE (p)))
+    {
+      pushdecl (TREE_VALUE (p));
+      p = TREE_CHAIN (p);
+    }
+}
+
 \f
 /* We have finished doing semantic analysis on DECL, but have not yet
    generated RTL for its body.  Save away our current state, so that
Index: gcc/cp/cp-tree.h
===================================================================
--- gcc/cp/cp-tree.h	(revision 213130)
+++ gcc/cp/cp-tree.h	(working copy)
@@ -5432,6 +5432,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);

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2014-07-30 11:49 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-06-17  9:06 [c++-concepts] Allow function parameters to be referenced in trailing requires clauses Braden Obrzut
2014-07-30 11:55 ` Andrew Sutton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).