summaryrefslogtreecommitdiff
path: root/clang/lib/Parse/ParseTemplate.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Parse/ParseTemplate.cpp')
-rw-r--r--clang/lib/Parse/ParseTemplate.cpp241
1 files changed, 201 insertions, 40 deletions
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index 928bc5aa25b3..1b9301b6591d 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -12,6 +12,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/Parse/ParseDiagnostic.h"
#include "clang/Parse/Parser.h"
#include "clang/Parse/RAIIObjectsForParser.h"
@@ -130,7 +131,9 @@ Decl *Parser::ParseTemplateDeclarationOrSpecialization(
if (TryConsumeToken(tok::kw_requires)) {
OptionalRequiresClauseConstraintER =
- Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression());
+ Actions.CorrectDelayedTyposInExpr(
+ ParseConstraintLogicalOrExpression(
+ /*IsTrailingRequiresClause=*/false));
if (!OptionalRequiresClauseConstraintER.isUsable()) {
// Skip until the semi-colon or a '}'.
SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
@@ -254,8 +257,12 @@ Decl *Parser::ParseSingleDeclarationAfterTemplate(
});
LateParsedAttrList LateParsedAttrs(true);
- if (DeclaratorInfo.isFunctionDeclarator())
+ if (DeclaratorInfo.isFunctionDeclarator()) {
+ if (Tok.is(tok::kw_requires))
+ ParseTrailingRequiresClause(DeclaratorInfo);
+
MaybeParseGNUAttributes(DeclaratorInfo, &LateParsedAttrs);
+ }
if (DeclaratorInfo.isFunctionDeclarator() &&
isStartOfFunctionDefinition(DeclaratorInfo)) {
@@ -492,7 +499,10 @@ Parser::ParseTemplateParameterList(const unsigned Depth,
/// Determine whether the parser is at the start of a template
/// type parameter.
-bool Parser::isStartOfTemplateTypeParameter() {
+/// \param ScopeError will receive true if there was an error parsing a
+/// scope specifier at the current location.
+bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) {
+ ScopeError = false;
if (Tok.is(tok::kw_class)) {
// "class" may be the start of an elaborated-type-specifier or a
// type-parameter. Per C++ [temp.param]p3, we prefer the type-parameter.
@@ -525,6 +535,40 @@ bool Parser::isStartOfTemplateTypeParameter() {
}
}
+ bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope);
+ CXXScopeSpec SS;
+ ScopeError =
+ 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);
+ if (ScopeError)
+ return false;
+ if (TryAnnotateTypeConstraint(SS))
+ return false;
+ bool IsTypeConstraint = isTypeConstraintAnnotation();
+ if (!IsTypeConstraint && SS.isNotEmpty()) {
+ // This isn't a type-constraint but we've already parsed this scope
+ // specifier - annotate it.
+ AnnotateScopeToken(SS, /*isNewAnnotation=*/!WasScopeAnnotation);
+ return false;
+ }
+
+ if (IsTypeConstraint &&
+ // Next token might be 'auto' or 'decltype', indicating that this
+ // type-constraint is in fact part of a placeholder-type-specifier of a
+ // non-type template parameter.
+ !NextToken().isOneOf(tok::kw_auto, tok::kw_decltype))
+ return true;
+
// 'typedef' is a reasonably-common typo/thinko for 'typename', and is
// ill-formed otherwise.
if (Tok.isNot(tok::kw_typename) && Tok.isNot(tok::kw_typedef))
@@ -568,24 +612,37 @@ bool Parser::isStartOfTemplateTypeParameter() {
/// type-parameter
/// parameter-declaration
///
-/// type-parameter: (see below)
-/// 'class' ...[opt] identifier[opt]
-/// 'class' identifier[opt] '=' type-id
-/// 'typename' ...[opt] identifier[opt]
-/// 'typename' identifier[opt] '=' type-id
-/// 'template' '<' template-parameter-list '>'
-/// 'class' ...[opt] identifier[opt]
-/// 'template' '<' template-parameter-list '>' 'class' identifier[opt]
-/// = id-expression
+/// type-parameter: (See below)
+/// type-parameter-key ...[opt] identifier[opt]
+/// type-parameter-key identifier[opt] = type-id
+/// (C++2a) type-constraint ...[opt] identifier[opt]
+/// (C++2a) type-constraint identifier[opt] = type-id
+/// 'template' '<' template-parameter-list '>' type-parameter-key
+/// ...[opt] identifier[opt]
+/// 'template' '<' template-parameter-list '>' type-parameter-key
+/// identifier[opt] '=' id-expression
+///
+/// type-parameter-key:
+/// class
+/// typename
+///
NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) {
- if (isStartOfTemplateTypeParameter()) {
- // Is there just a typo in the input code? ('typedef' instead of 'typename')
+ // We could be facing a type-constraint, which (could) start a type parameter.
+ // Annotate it now (we might end up not using it if we determine this
+ // type-constraint is in fact part of a placeholder-type-specifier of a
+ // non-type template parameter.
+
+ bool ScopeError;
+ if (isStartOfTemplateTypeParameter(ScopeError)) {
+ // Is there just a typo in the input code? ('typedef' instead of
+ // 'typename')
if (Tok.is(tok::kw_typedef)) {
Diag(Tok.getLocation(), diag::err_expected_template_parameter);
Diag(Tok.getLocation(), diag::note_meant_to_use_typename)
<< FixItHint::CreateReplacement(CharSourceRange::getCharRange(
- Tok.getLocation(), Tok.getEndLoc()),
+ Tok.getLocation(),
+ Tok.getEndLoc()),
"typename");
Tok.setKind(tok::kw_typename);
@@ -593,7 +650,26 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) {
return ParseTypeParameter(Depth, Position);
}
-
+ if (ScopeError) {
+ // We return an invalid parameter as opposed to null to avoid having bogus
+ // diagnostics about an empty template parameter list.
+ // FIXME: Fix ParseTemplateParameterList to better handle nullptr results
+ // from here.
+ // Return a NTTP as if there was an error in a scope specifier, the user
+ // probably meant to write the type of a NTTP.
+ DeclSpec DS(getAttrFactory());
+ DS.SetTypeSpecError();
+ Declarator D(DS, DeclaratorContext::TemplateParamContext);
+ D.SetIdentifier(nullptr, Tok.getLocation());
+ D.setInvalidType(true);
+ NamedDecl *ErrorParam = Actions.ActOnNonTypeTemplateParameter(
+ getCurScope(), D, Depth, Position, /*EqualLoc=*/SourceLocation(),
+ /*DefaultArg=*/nullptr);
+ ErrorParam->setInvalidDecl(true);
+ SkipUntil(tok::comma, tok::greater, tok::greatergreater,
+ StopAtSemi | StopBeforeMatch);
+ return ErrorParam;
+ }
if (Tok.is(tok::kw_template))
return ParseTemplateTemplateParameter(Depth, Position);
@@ -603,6 +679,56 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) {
return ParseNonTypeTemplateParameter(Depth, Position);
}
+/// Check whether the current token is a template-id annotation denoting a
+/// type-constraint.
+bool Parser::isTypeConstraintAnnotation() {
+ if (Tok.isNot(tok::annot_template_id))
+ return false;
+ const auto *ExistingAnnot =
+ static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
+ return ExistingAnnot->Kind == TNK_Concept_template;
+}
+
+/// Try parsing a type-constraint construct at the current location, after the
+/// optional scope specifier.
+///
+/// type-constraint:
+/// nested-name-specifier[opt] concept-name
+/// nested-name-specifier[opt] concept-name
+/// '<' template-argument-list[opt] '>'[opt]
+///
+/// \returns true if an error occurred, and false otherwise.
+bool Parser::TryAnnotateTypeConstraint(CXXScopeSpec &SS) {
+ if (!getLangOpts().ConceptsTS || Tok.isNot(tok::identifier))
+ return false;
+
+ UnqualifiedId PossibleConceptName;
+ PossibleConceptName.setIdentifier(Tok.getIdentifierInfo(),
+ Tok.getLocation());
+
+ TemplateTy PossibleConcept;
+ bool MemberOfUnknownSpecialization = false;
+ auto TNK = Actions.isTemplateName(getCurScope(), SS,
+ /*hasTemplateKeyword=*/false,
+ PossibleConceptName,
+ /*ObjectType=*/ParsedType(),
+ /*EnteringContext=*/false,
+ PossibleConcept,
+ MemberOfUnknownSpecialization);
+ assert(!MemberOfUnknownSpecialization
+ && "Member when we only allowed namespace scope qualifiers??");
+ if (!PossibleConcept || TNK != TNK_Concept_template)
+ return false;
+
+ // At this point we're sure we're dealing with a constrained parameter. It
+ // may or may not have a template parameter list following the concept name.
+ return AnnotateTemplateIdToken(PossibleConcept, TNK, SS,
+ /*TemplateKWLoc=*/SourceLocation(),
+ PossibleConceptName,
+ /*AllowTypeAnnotation=*/false,
+ /*TypeConstraint=*/true);
+}
+
/// ParseTypeParameter - Parse a template type parameter (C++ [temp.param]).
/// Other kinds of template parameters are parsed in
/// ParseTemplateTemplateParameter and ParseNonTypeTemplateParameter.
@@ -613,12 +739,25 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) {
/// 'typename' ...[opt][C++0x] identifier[opt]
/// 'typename' identifier[opt] '=' type-id
NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) {
- assert(Tok.isOneOf(tok::kw_class, tok::kw_typename) &&
- "A type-parameter starts with 'class' or 'typename'");
-
- // Consume the 'class' or 'typename' keyword.
- bool TypenameKeyword = Tok.is(tok::kw_typename);
- SourceLocation KeyLoc = ConsumeToken();
+ assert(Tok.isOneOf(tok::kw_class, tok::kw_typename, tok::annot_template_id) &&
+ "A type-parameter starts with 'class', 'typename' or a "
+ "type-constraint");
+
+ TemplateIdAnnotation *TypeConstraint = nullptr;
+ bool TypenameKeyword = false;
+ SourceLocation KeyLoc;
+ if (Tok.is(tok::annot_template_id)) {
+ // Consume the 'type-constraint'.
+ TypeConstraint =
+ static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
+ assert(TypeConstraint->Kind == TNK_Concept_template &&
+ "stray non-concept template-id annotation");
+ KeyLoc = ConsumeAnnotationToken();
+ } else {
+ // Consume the 'class' or 'typename' keyword.
+ TypenameKeyword = Tok.is(tok::kw_typename);
+ KeyLoc = ConsumeToken();
+ }
// Grab the ellipsis (if given).
SourceLocation EllipsisLoc;
@@ -658,9 +797,19 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) {
DefaultArg = ParseTypeName(/*Range=*/nullptr,
DeclaratorContext::TemplateTypeArgContext).get();
- return Actions.ActOnTypeParameter(getCurScope(), TypenameKeyword, EllipsisLoc,
- KeyLoc, ParamName, NameLoc, Depth, Position,
- EqualLoc, DefaultArg);
+ NamedDecl *NewDecl = Actions.ActOnTypeParameter(getCurScope(),
+ TypenameKeyword, EllipsisLoc,
+ KeyLoc, ParamName, NameLoc,
+ Depth, Position, EqualLoc,
+ DefaultArg,
+ TypeConstraint != nullptr);
+
+ if (TypeConstraint)
+ Actions.ActOnTypeConstraint(TypeConstraint,
+ cast<TemplateTypeParmDecl>(NewDecl),
+ EllipsisLoc);
+
+ return NewDecl;
}
/// ParseTemplateTemplateParameter - Handle the parsing of template
@@ -823,7 +972,7 @@ Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) {
// Create the parameter.
return Actions.ActOnNonTypeTemplateParameter(getCurScope(), ParamDecl,
- Depth, Position, EqualLoc,
+ Depth, Position, EqualLoc,
DefaultArg.get());
}
@@ -1099,6 +1248,10 @@ Parser::ParseTemplateIdAfterTemplateName(bool ConsumeLastToken,
/// simple-template-id is always replaced with a template-id
/// annotation token.
///
+/// \param TypeConstraint if true, then this is actually a type-constraint,
+/// meaning that the template argument list can be omitted (and the template in
+/// question must be a concept).
+///
/// If an unrecoverable parse error occurs and no annotation token can be
/// formed, this function returns true.
///
@@ -1106,10 +1259,15 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
UnqualifiedId &TemplateName,
- bool AllowTypeAnnotation) {
+ bool AllowTypeAnnotation,
+ bool TypeConstraint) {
assert(getLangOpts().CPlusPlus && "Can only annotate template-ids in C++");
- assert(Template && Tok.is(tok::less) &&
+ assert(Template && (Tok.is(tok::less) || TypeConstraint) &&
"Parser isn't at the beginning of a template-id");
+ assert(!(TypeConstraint && AllowTypeAnnotation) && "type-constraint can't be "
+ "a type annotation");
+ assert((!TypeConstraint || TNK == TNK_Concept_template) && "type-constraint "
+ "must accompany a concept name");
// Consume the template-name.
SourceLocation TemplateNameLoc = TemplateName.getSourceRange().getBegin();
@@ -1117,17 +1275,19 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
// Parse the enclosed template argument list.
SourceLocation LAngleLoc, RAngleLoc;
TemplateArgList TemplateArgs;
- bool Invalid = ParseTemplateIdAfterTemplateName(false, LAngleLoc,
- TemplateArgs,
- RAngleLoc);
-
- if (Invalid) {
- // If we failed to parse the template ID but skipped ahead to a >, we're not
- // going to be able to form a token annotation. Eat the '>' if present.
- TryConsumeToken(tok::greater);
- // FIXME: Annotate the token stream so we don't produce the same errors
- // again if we're doing this annotation as part of a tentative parse.
- return true;
+ if (!TypeConstraint || Tok.is(tok::less)) {
+ bool Invalid = ParseTemplateIdAfterTemplateName(false, LAngleLoc,
+ TemplateArgs,
+ RAngleLoc);
+
+ if (Invalid) {
+ // If we failed to parse the template ID but skipped ahead to a >, we're not
+ // going to be able to form a token annotation. Eat the '>' if present.
+ TryConsumeToken(tok::greater);
+ // FIXME: Annotate the token stream so we don't produce the same errors
+ // again if we're doing this annotation as part of a tentative parse.
+ return true;
+ }
}
ASTTemplateArgsPtr TemplateArgsPtr(TemplateArgs);
@@ -1365,8 +1525,9 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() {
// Parse a non-type template argument.
SourceLocation Loc = Tok.getLocation();
ExprResult ExprArg = ParseConstantExpressionInExprEvalContext(MaybeTypeCast);
- if (ExprArg.isInvalid() || !ExprArg.get())
+ if (ExprArg.isInvalid() || !ExprArg.get()) {
return ParsedTemplateArgument();
+ }
return ParsedTemplateArgument(ParsedTemplateArgument::NonType,
ExprArg.get(), Loc);