Adds additional checks and tests for ill-formed programs.
2014-06-12 Andrew Sutton <[email protected]>
* 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" }
+}