forked from OSchip/llvm-project
[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:
parent
6a75acb1c2
commit
ec3cb573f5
|
@ -1331,6 +1331,7 @@ public:
|
||||||
|
|
||||||
ExprResult ParseExpression(TypeCastState isTypeCast = NotTypeCast);
|
ExprResult ParseExpression(TypeCastState isTypeCast = NotTypeCast);
|
||||||
ExprResult ParseConstantExpression(TypeCastState isTypeCast = NotTypeCast);
|
ExprResult ParseConstantExpression(TypeCastState isTypeCast = NotTypeCast);
|
||||||
|
ExprResult ParseConstraintExpression();
|
||||||
// Expr that doesn't include commas.
|
// Expr that doesn't include commas.
|
||||||
ExprResult ParseAssignmentExpression(TypeCastState isTypeCast = NotTypeCast);
|
ExprResult ParseAssignmentExpression(TypeCastState isTypeCast = NotTypeCast);
|
||||||
|
|
||||||
|
|
|
@ -205,6 +205,24 @@ ExprResult Parser::ParseConstantExpression(TypeCastState isTypeCast) {
|
||||||
return Actions.ActOnConstantExpression(Res);
|
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() {
|
bool Parser::isNotExpressionStart() {
|
||||||
tok::TokenKind K = Tok.getKind();
|
tok::TokenKind K = Tok.getKind();
|
||||||
if (K == tok::l_brace || K == tok::r_brace ||
|
if (K == tok::l_brace || K == tok::r_brace ||
|
||||||
|
|
|
@ -116,7 +116,7 @@ Parser::ParseTemplateDeclarationOrSpecialization(unsigned Context,
|
||||||
SmallVector<Decl*, 4> TemplateParams;
|
SmallVector<Decl*, 4> TemplateParams;
|
||||||
if (ParseTemplateParameters(CurTemplateDepthTracker.getDepth(),
|
if (ParseTemplateParameters(CurTemplateDepthTracker.getDepth(),
|
||||||
TemplateParams, LAngleLoc, RAngleLoc)) {
|
TemplateParams, LAngleLoc, RAngleLoc)) {
|
||||||
// Skip until the semi-colon or a }.
|
// Skip until the semi-colon or a '}'.
|
||||||
SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
|
SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
|
||||||
TryConsumeToken(tok::semi);
|
TryConsumeToken(tok::semi);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -132,6 +132,17 @@ Parser::ParseTemplateDeclarationOrSpecialization(unsigned Context,
|
||||||
if (!TemplateParams.empty()) {
|
if (!TemplateParams.empty()) {
|
||||||
isSpecialization = false;
|
isSpecialization = false;
|
||||||
++CurTemplateDepthTracker;
|
++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 {
|
} else {
|
||||||
LastParamListWasEmpty = true;
|
LastParamListWasEmpty = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}}
|
||||||
|
};
|
|
@ -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;
|
Loading…
Reference in New Issue