Adds additional checks and tests for ill-formed programs.

2014-06-12  Andrew Sutton  <andrew.n.sut...@gmail.com>
        * gcc/cp/parser.c (cp_check_type_concept): New.
        (cp_check_concept_name): Remove redundant condition from check.
        Diagnose misuse of non-type concepts in constrained type specifiers.
        * gcc/testuite/g++.dg/concepts/generic-fn.C: Add tests for
        non-simple constrained-type-specifiers and nested-name-specifiers
        in concept names.
        * gcc/testuite/g++.dg/concepts/generic-fn-err.C: New tests for
        diagnosing ill-formed programs.

Committed in r211585.

Andrew Sutton
Index: parser.c
===================================================================
--- parser.c	(revision 211476)
+++ parser.c	(working copy)
@@ -15132,11 +15132,22 @@ cp_parser_type_name (cp_parser* parser)
   return type_decl;
 }
 
-
+/// Returns true if proto is a type parameter, but not a template template
+/// parameter.
+static bool
+cp_check_type_concept (tree proto, tree fn) 
+{
+  if (TREE_CODE (proto) != TYPE_DECL) 
+    {
+      error ("invalid use of non-type concept %qD", fn);
+      return false;
+    }
+  return true;
+}
 
 // If DECL refers to a concept, return a TYPE_DECL representing the result
 // of using the constrained type specifier in the current context. 
-
+//
 // DECL refers to a concept if
 //   - it is an overload set containing a function concept taking a single
 //     type argument, or
@@ -15173,9 +15184,13 @@ cp_check_concept_name (cp_parser* parser
 
   // In template paramteer scope, this results in a constrained parameter.
   // Return a descriptor of that parm.
-  if (template_parm_scope_p () && processing_template_parmlist)
+  if (processing_template_parmlist)
     return build_constrained_parameter (proto, fn);
 
+  // In any other context, a concept must be a type concept.
+  if (!cp_check_type_concept (proto, fn))
+    return error_mark_node;
+
   // In a parameter-declaration-clause, constrained-type specifiers
   // result in invented template parameters.
   if (parser->auto_is_implicit_function_template_parm_p)
Index: generic-fn.C
===================================================================
--- generic-fn.C	(revision 211476)
+++ generic-fn.C	(working copy)
@@ -1,11 +1,16 @@
+// { dg-do run }
 // { dg-options "-std=c++1y" }
 
 #include <cassert>
+#include <type_traits>
 
 template<typename T>
   concept bool C() { return __is_class(T); }
 
-struct S { } s;
+template<typename T>
+  concept bool Type() { return true; }
+
+struct S { };
 
 int called;
 
@@ -50,7 +55,43 @@ template<C T>
   };
 
 
+void ptr(C*) { called = 1; }
+void ptr(const C*) { called = 2; }
+
+void ref(C&) { called = 1; }
+void ref(const C&) { called = 2; }
+
+void 
+fwd_lvalue_ref(Type&& x) {
+  using T = decltype(x);
+  static_assert(std::is_lvalue_reference<T>::value, "not an lvlaue reference");
+}
+
+void 
+fwd_const_lvalue_ref(Type&& x) {
+  using T = decltype(x);
+  static_assert(std::is_lvalue_reference<T>::value, "not an lvalue reference");
+  using U = typename std::remove_reference<T>::type;
+  static_assert(std::is_const<U>::value, "not const-qualified");
+}
+
+void fwd_rvalue_ref(Type&& x) {
+  using T = decltype(x);
+  static_assert(std::is_rvalue_reference<T>::value, "not an rvalue reference");
+}
+
+// Make sure we can use nested names speicifers for concept names.
+namespace N {
+  template<typename T>
+    concept bool C() { return true; }
+} // namesspace N
+
+void foo(N::C x) { }
+
 int main() {
+  S s;
+  const S cs;
+
   f(0); assert(called == 1);
   g(s); assert(called == 2);
 
@@ -60,7 +101,6 @@ int main() {
   S1 s1;
   s1.f1(0); assert(called == 1);
   s1.f2(s); assert(called == 2);
-  // s1.f2(0); // Error
 
   s1.f3(0); assert(called == 1);
   s1.f3(s); assert(called == 2);
@@ -68,26 +108,35 @@ int main() {
   S2<S> s2;
   s2.f1(0); assert(called == 1);
   s2.f2(s); assert(called == 2);
-  // s2.f2(0); // Error
 
   s2.f3(0); assert(called == 1);
   s2.f3(s); assert(called == 2);
 
   s2.h1(0); assert(called == 1);
   s2.h2(s); assert(called == 2);
-  // s2.h2(0); // Error
 
   s2.h3(0); assert(called == 1);
   s2.h3(s); assert(called == 2);
 
   s2.g1(s, s); assert(called == 1); 
-  // s2.g(s, 0); // Error
-  // s2.g(0, s); // Error
-
   s2.g2(s, s); assert(called == 2);
-  // s2.g(s, 0); // Error
+
+  ptr(&s); assert(called == 1);
+  ptr(&cs); assert(called == 2);
+
+  ref(s); assert(called == 1);
+  ref(cs); assert(called == 2);
+
+  // Check forwarding problems
+  fwd_lvalue_ref(s);
+  fwd_const_lvalue_ref(cs);
+  fwd_rvalue_ref(S());
+
+  foo(0);
 }
 
+// Test that decl/def matching works.
+
 void p(auto x) { called = 1; }
 void p(C x) { called = 2; }
 
Index: generic-fn-err.C
===================================================================
--- generic-fn-err.C	(revision 0)
+++ generic-fn-err.C	(revision 0)
@@ -0,0 +1,51 @@
+// { dg-options "-std=c++1y" }
+
+#include <cassert>
+
+template<typename T>
+  concept bool C() { return __is_class(T); }
+
+template<int N>
+  concept bool Int() { return true; }
+
+template<template<typename> class X>
+  concept bool Template() { return true; }
+
+struct S { };
+
+void f1(Int) { }      // { dg-error "invalid" }
+void f2(Template) { } // { dg-error "invalid" }
+
+struct S1 {
+  void f1(auto x) { }
+  void f2(C x) { }
+
+  void f3(auto x) { }
+  void f3(C x) { }
+};
+
+template<C T>
+  struct S2 {
+    void f1(auto x) { }
+    void f2(C x) { }
+
+    void h1(auto x);
+    void h2(C x);
+
+    template<C U>
+      void g(T t, U u) { }
+  };
+
+int main() {
+  S s;
+
+  S1 s1;
+  s1.f2(0); // { dg-error "matching" }
+
+  S2<S> s2;
+  s2.f2(0); // { dg-error "matching" }
+  s2.h2(0); // { dg-error "matching" }
+
+  s2.g(s, 0); // { dg-error "matching" }
+  s2.g(0, s); // { dg-error "matching" }
+}

Reply via email to