[Concepts] Parsing of requires-clause in template-declaration

Summary:
This change implements parse-only acceptance of the optional
requires-clause in a template-declaration. Diagnostic testing is added
for cases where the grammar is ambiguous with the expectation that the
longest token sequence which matches the syntax of a
constraint-expression is consumed without backtracking.

Reviewers: faisalv, fraggamuffin, rsmith

Reviewed By: rsmith

Subscribers: cfe-commits

Differential Revision: http://reviews.llvm.org/D10462

llvm-svn: 240611
This commit is contained in:
Hubert Tong 2015-06-25 00:23:39 +00:00
parent 6a75acb1c2
commit ec3cb573f5
5 changed files with 142 additions and 1 deletions

View File

@ -1331,6 +1331,7 @@ public:
ExprResult ParseExpression(TypeCastState isTypeCast = NotTypeCast);
ExprResult ParseConstantExpression(TypeCastState isTypeCast = NotTypeCast);
ExprResult ParseConstraintExpression();
// Expr that doesn't include commas.
ExprResult ParseAssignmentExpression(TypeCastState isTypeCast = NotTypeCast);

View File

@ -205,6 +205,24 @@ ExprResult Parser::ParseConstantExpression(TypeCastState isTypeCast) {
return Actions.ActOnConstantExpression(Res);
}
/// \brief Parse a constraint-expression.
///
/// \verbatim
/// constraint-expression: [Concepts TS temp.constr.decl p1]
/// logical-or-expression
/// \endverbatim
ExprResult Parser::ParseConstraintExpression() {
// FIXME: this may erroneously consume a function-body as the braced
// initializer list of a compound literal
//
// FIXME: this may erroneously consume a parenthesized rvalue reference
// declarator as a parenthesized address-of-label expression
ExprResult LHS(ParseCastExpression(/*isUnaryExpression=*/false));
ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::LogicalOr));
return Res;
}
bool Parser::isNotExpressionStart() {
tok::TokenKind K = Tok.getKind();
if (K == tok::l_brace || K == tok::r_brace ||

View File

@ -116,7 +116,7 @@ Parser::ParseTemplateDeclarationOrSpecialization(unsigned Context,
SmallVector<Decl*, 4> TemplateParams;
if (ParseTemplateParameters(CurTemplateDepthTracker.getDepth(),
TemplateParams, LAngleLoc, RAngleLoc)) {
// Skip until the semi-colon or a }.
// Skip until the semi-colon or a '}'.
SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
TryConsumeToken(tok::semi);
return nullptr;
@ -132,6 +132,17 @@ Parser::ParseTemplateDeclarationOrSpecialization(unsigned Context,
if (!TemplateParams.empty()) {
isSpecialization = false;
++CurTemplateDepthTracker;
if (TryConsumeToken(tok::kw_requires)) {
ExprResult ER =
Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression());
if (!ER.isUsable()) {
// Skip until the semi-colon or a '}'.
SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
TryConsumeToken(tok::semi);
return nullptr;
}
}
} else {
LastParamListWasEmpty = true;
}

View File

@ -0,0 +1,29 @@
// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ %s -verify
// Test parsing of constraint-expressions in cases where the grammar is
// ambiguous with the expectation that the longest token sequence which matches
// the syntax is consumed without backtracking.
// type-specifier-seq in conversion-type-id
template <typename T> requires (bool)&T::operator short
unsigned int foo(); // expected-error {{C++ requires a type specifier for all declarations}}
// type-specifier-seq in new-type-id
template <typename T> requires (bool)sizeof new (T::f()) short
unsigned int bar(); // expected-error {{C++ requires a type specifier for all declarations}}
template<typename T> requires (bool)sizeof new (T::f()) unsigned // expected-error {{'struct' cannot be signed or unsigned}}
struct X { }; // expected-error {{'X' cannot be defined in a type specifier}}
// C-style cast
// of function call on function-style cast
template <typename T> requires (bool(T()))
T (*fp)(); // expected-error {{use of undeclared identifier 'fp'}}
// function-style cast
// as the callee in a function call
struct A {
static int t;
template <typename T> requires bool(T())
(A(T (&t))) { } // expected-error {{called object type 'bool' is not a function or function pointer}}
};

View File

@ -0,0 +1,82 @@
// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ %s -verify
// expected-no-diagnostics
// Test parsing of the optional requires-clause in a template-declaration.
template <typename T> requires true
void foo() { }
template <typename T> requires !0
struct A {
void foo();
struct AA;
enum E : int;
static int x;
template <typename> requires true
void Mfoo();
template <typename> requires true
struct M;
template <typename> requires true
static int Mx;
template <typename TT> requires true
using MQ = M<TT>;
};
template <typename T> requires !0
void A<T>::foo() { }
template <typename T> requires !0
struct A<T>::AA { };
template <typename T> requires !0
enum A<T>::E : int { E0 };
template <typename T> requires !0
int A<T>::x = 0;
template <typename T> requires !0
template <typename> requires true
void A<T>::Mfoo() { }
template <typename T> requires !0
template <typename> requires true
struct A<T>::M { };
template <typename T> requires !0
template <typename> requires true
int A<T>::Mx = 0;
template <typename T> requires true
int x = 0;
template <typename T> requires true
using Q = A<T>;
struct C {
template <typename> requires true
void Mfoo();
template <typename> requires true
struct M;
template <typename> requires true
static int Mx;
template <typename T> requires true
using MQ = M<T>;
};
template <typename> requires true
void C::Mfoo() { }
template <typename> requires true
struct C::M { };
template <typename> requires true
int C::Mx = 0;