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" } +}