[Concepts] Fix parsing of scope specifier in compound-requirements, add more tests for scope specifiers in type-constraints

The code for parsing of type-constraints in compound-requirements was not adapted for the new TryAnnotateTypeConstraint which
caused compound-requirements with scope specifiers to ignore them.

Also add regression tests for scope specifiers in type-constraints in more contexts.
This commit is contained in:
Saar Raz 2020-01-26 20:39:44 +02:00
parent f29204d388
commit 5043962dd3
4 changed files with 62 additions and 44 deletions

View File

@ -3383,25 +3383,6 @@ ExprResult Parser::ParseRequiresExpression() {
Diag(Tok, diag::err_requires_expr_missing_arrow)
<< FixItHint::CreateInsertion(Tok.getLocation(), "->");
// Try to parse a 'type-constraint'
CXXScopeSpec SS;
if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(),
/*EnteringContext=*/false,
/*MayBePseudoDestructor=*/nullptr,
// If this is not a type-constraint,
// then this scope-spec is part of
// the typename of a non-type
// template parameter
/*IsTypename=*/true,
/*LastII=*/nullptr,
// We won't find concepts in
// non-namespaces anyway, so might as
// well parse this correctly for
// possible type names.
/*OnlyNamespace=*/false,
/*SuppressDiagnostic=*/true)) {
SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
break;
}
if (TryAnnotateTypeConstraint()) {
SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
break;
@ -3411,8 +3392,13 @@ ExprResult Parser::ParseRequiresExpression() {
SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
break;
}
if (Tok.is(tok::annot_cxxscope))
CXXScopeSpec SS;
if (Tok.is(tok::annot_cxxscope)) {
Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(),
Tok.getAnnotationRange(),
SS);
ConsumeAnnotationToken();
}
Req = Actions.ActOnCompoundRequirement(
Expression.get(), NoexceptLoc, SS, takeTemplateIdAnnotation(Tok),

View File

@ -0,0 +1,19 @@
// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify
// expected-no-diagnostics
template<typename T, typename U=void>
concept C = true;
namespace ns {
template<typename T, typename U=void>
concept D = true;
}
void foo(C auto a,
C<int> auto b,
ns::D auto c,
ns::D<int> auto d,
const C auto e,
const C<int> auto f,
const ns::D auto g,
const ns::D<int> auto h);

View File

@ -108,34 +108,38 @@ bool r29 = requires { { 0 } noexcept C1; };
bool r30 = requires { { 0 } noexcept -> C2<int>; };
namespace ns { template<typename T> concept C = true; }
bool r31 = requires { { 0 } noexcept -> ns::C; };
template<typename T>
T i1 = 0;
bool r31 = requires { requires false, 1; };
bool r32 = requires { requires false, 1; };
// expected-error@-1 {{expected ';' at end of requirement}}
bool r32 = requires { 0 noexcept; };
bool r33 = requires { 0 noexcept; };
// expected-error@-1 {{'noexcept' can only be used in a compound requirement (with '{' '}' around the expression)}}
bool r33 = requires { 0 int; };
bool r34 = requires { 0 int; };
// expected-error@-1 {{expected ';' at end of requirement}}
bool r34 = requires { requires true };
bool r35 = requires { requires true };
// expected-error@-1 {{expected ';' at end of requirement}}
bool r35 = requires (bool b) { requires sizeof(b) == 1; };
bool r36 = requires (bool b) { requires sizeof(b) == 1; };
void r36(bool b) requires requires { 1 } {}
void r37(bool b) requires requires { 1 } {}
// expected-error@-1 {{expected ';' at end of requirement}}
bool r37 = requires { requires { 1; }; };
bool r38 = requires { requires { 1; }; };
// expected-warning@-1 {{this requires expression will only be checked for syntactic validity; did you intend to place it in a nested requirement? (add another 'requires' before the expression)}}
bool r38 = requires { requires () { 1; }; };
bool r39 = requires { requires () { 1; }; };
// expected-warning@-1 {{this requires expression will only be checked for syntactic validity; did you intend to place it in a nested requirement? (add another 'requires' before the expression)}}
bool r39 = requires { requires (int i) { i; }; };
bool r40 = requires { requires (int i) { i; }; };
// expected-warning@-1 {{this requires expression will only be checked for syntactic validity; did you intend to place it in a nested requirement? (add another 'requires' before the expression)}}
bool r40 = requires { requires (); };
bool r41 = requires { requires (); };
// expected-error@-1 {{expected expression}}

View File

@ -3,24 +3,33 @@
template<typename T, typename U=void>
concept C = true;
namespace ns {
template<typename T, typename U=void>
concept D = true;
}
int foo() {
C auto a4 = 1;
C<> auto a5 = 1;
C<int> auto a6 = 1;
const C auto &a7 = 1;
const C<> auto &a8 = 1;
const C<int> auto &a9 = 1;
C decltype(auto) a10 = 1;
C<> decltype(auto) a11 = 1;
C<int> decltype(auto) a12 = 1;
const C<> decltype(auto) &a13 = 1; // expected-error{{'decltype(auto)' cannot be combined with other type specifiers}}
{ns::D auto a = 1;}
{C auto a = 1;}
{C<> auto a = 1;}
{C<int> auto a = 1;}
{ns::D<int> auto a = 1;}
{const ns::D auto &a = 1;}
{const C auto &a = 1;}
{const C<> auto &a = 1;}
{const C<int> auto &a = 1;}
{const ns::D<int> auto &a = 1;}
{C decltype(auto) a = 1;}
{C<> decltype(auto) a = 1;}
{C<int> decltype(auto) a = 1;}
{const C<> decltype(auto) &a = 1;} // expected-error{{'decltype(auto)' cannot be combined with other type specifiers}}
// expected-error@-1{{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
const C<int> decltype(auto) &a14 = 1; // expected-error{{'decltype(auto)' cannot be combined with other type specifiers}}
{const C<int> decltype(auto) &a = 1;} // expected-error{{'decltype(auto)' cannot be combined with other type specifiers}}
// expected-error@-1{{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
C a15 = 1;
{C a = 1;}
// expected-error@-1{{expected 'auto' or 'decltype(auto)' after concept name}}
C decltype a19 = 1;
{C decltype a19 = 1;}
// expected-error@-1{{expected '('}}
C decltype(1) a20 = 1;
{C decltype(1) a20 = 1;}
// expected-error@-1{{expected 'auto' or 'decltype(auto)' after concept name}}
}