aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Parse/ParseExprCXX.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Parse/ParseExprCXX.cpp')
-rw-r--r--clang/lib/Parse/ParseExprCXX.cpp713
1 files changed, 545 insertions, 168 deletions
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index e685d5ea8a9c..aa35200c33b6 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -11,7 +11,9 @@
//===----------------------------------------------------------------------===//
#include "clang/Parse/Parser.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/Basic/PrettyStackTrace.h"
#include "clang/Lex/LiteralSupport.h"
#include "clang/Parse/ParseDiagnostic.h"
@@ -29,10 +31,11 @@ static int SelectDigraphErrorMessage(tok::TokenKind Kind) {
// template name
case tok::unknown: return 0;
// casts
- case tok::kw_const_cast: return 1;
- case tok::kw_dynamic_cast: return 2;
- case tok::kw_reinterpret_cast: return 3;
- case tok::kw_static_cast: return 4;
+ case tok::kw_addrspace_cast: return 1;
+ case tok::kw_const_cast: return 2;
+ case tok::kw_dynamic_cast: return 3;
+ case tok::kw_reinterpret_cast: return 4;
+ case tok::kw_static_cast: return 5;
default:
llvm_unreachable("Unknown type for digraph error message.");
}
@@ -122,13 +125,17 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType,
/// the "." or "->" of a member access expression, this parameter provides the
/// type of the object whose members are being accessed.
///
+/// \param ObjectHadErrors if this unqualified-id occurs within a member access
+/// expression, indicates whether the original subexpressions had any errors.
+/// When true, diagnostics for missing 'template' keyword will be supressed.
+///
/// \param EnteringContext whether we will be entering into the context of
/// the nested-name-specifier after parsing it.
///
/// \param MayBePseudoDestructor When non-NULL, points to a flag that
/// indicates whether this nested-name-specifier may be part of a
/// pseudo-destructor name. In this case, the flag will be set false
-/// if we don't actually end up parsing a destructor name. Moreorover,
+/// if we don't actually end up parsing a destructor name. Moreover,
/// if we do end up determining that we are parsing a destructor name,
/// the last component of the nested-name-specifier is not parsed as
/// part of the scope specifier.
@@ -144,14 +151,10 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType,
///
///
/// \returns true if there was an error parsing a scope specifier
-bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
- ParsedType ObjectType,
- bool EnteringContext,
- bool *MayBePseudoDestructor,
- bool IsTypename,
- IdentifierInfo **LastII,
- bool OnlyNamespace,
- bool InUsingDeclaration) {
+bool Parser::ParseOptionalCXXScopeSpecifier(
+ CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors,
+ bool EnteringContext, bool *MayBePseudoDestructor, bool IsTypename,
+ IdentifierInfo **LastII, bool OnlyNamespace, bool InUsingDeclaration) {
assert(getLangOpts().CPlusPlus &&
"Call sites of this function should be guarded by checking for C++");
@@ -165,13 +168,6 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
return false;
}
- if (Tok.is(tok::annot_template_id)) {
- // If the current token is an annotated template id, it may already have
- // a scope specifier. Restore it.
- TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
- SS = TemplateId->SS;
- }
-
// Has to happen before any "return false"s in this function.
bool CheckForDestructor = false;
if (MayBePseudoDestructor && *MayBePseudoDestructor) {
@@ -321,13 +317,11 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
// Commit to parsing the template-id.
TPA.Commit();
TemplateTy Template;
- if (TemplateNameKind TNK = Actions.ActOnDependentTemplateName(
- getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType,
- EnteringContext, Template, /*AllowInjectedClassName*/ true)) {
- if (AnnotateTemplateIdToken(Template, TNK, SS, TemplateKWLoc,
- TemplateName, false))
- return true;
- } else
+ TemplateNameKind TNK = Actions.ActOnTemplateName(
+ getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType,
+ EnteringContext, Template, /*AllowInjectedClassName*/ true);
+ if (AnnotateTemplateIdToken(Template, TNK, SS, TemplateKWLoc,
+ TemplateName, false))
return true;
continue;
@@ -361,7 +355,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
TemplateId->NumArgs);
- if (Actions.ActOnCXXNestedNameSpecifier(getCurScope(),
+ if (TemplateId->isInvalid() ||
+ Actions.ActOnCXXNestedNameSpecifier(getCurScope(),
SS,
TemplateId->TemplateKWLoc,
TemplateId->Template,
@@ -423,8 +418,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
}
if (Next.is(tok::coloncolon)) {
- if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde) &&
- !Actions.isNonTypeNestedNameSpecifier(getCurScope(), SS, IdInfo)) {
+ if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde)) {
*MayBePseudoDestructor = true;
return false;
}
@@ -517,28 +511,29 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
if (MemberOfUnknownSpecialization && (ObjectType || SS.isSet()) &&
(IsTypename || isTemplateArgumentList(1) == TPResult::True)) {
- // We have something like t::getAs<T>, where getAs is a
- // member of an unknown specialization. However, this will only
- // parse correctly as a template, so suggest the keyword 'template'
- // before 'getAs' and treat this as a dependent template name.
- unsigned DiagID = diag::err_missing_dependent_template_keyword;
- if (getLangOpts().MicrosoftExt)
- DiagID = diag::warn_missing_dependent_template_keyword;
-
- Diag(Tok.getLocation(), DiagID)
- << II.getName()
- << FixItHint::CreateInsertion(Tok.getLocation(), "template ");
-
- if (TemplateNameKind TNK = Actions.ActOnDependentTemplateName(
- getCurScope(), SS, Tok.getLocation(), TemplateName, ObjectType,
- EnteringContext, Template, /*AllowInjectedClassName*/ true)) {
- // Consume the identifier.
- ConsumeToken();
- if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(),
- TemplateName, false))
- return true;
+ // If we had errors before, ObjectType can be dependent even without any
+ // templates. Do not report missing template keyword in that case.
+ if (!ObjectHadErrors) {
+ // We have something like t::getAs<T>, where getAs is a
+ // member of an unknown specialization. However, this will only
+ // parse correctly as a template, so suggest the keyword 'template'
+ // before 'getAs' and treat this as a dependent template name.
+ unsigned DiagID = diag::err_missing_dependent_template_keyword;
+ if (getLangOpts().MicrosoftExt)
+ DiagID = diag::warn_missing_dependent_template_keyword;
+
+ Diag(Tok.getLocation(), DiagID)
+ << II.getName()
+ << FixItHint::CreateInsertion(Tok.getLocation(), "template ");
}
- else
+
+ SourceLocation TemplateNameLoc = ConsumeToken();
+
+ TemplateNameKind TNK = Actions.ActOnTemplateName(
+ getCurScope(), SS, TemplateNameLoc, TemplateName, ObjectType,
+ EnteringContext, Template, /*AllowInjectedClassName*/ true);
+ if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(),
+ TemplateName, false))
return true;
continue;
@@ -553,7 +548,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
// Even if we didn't see any pieces of a nested-name-specifier, we
// still check whether there is a tilde in this position, which
// indicates a potential pseudo-destructor.
- if (CheckForDestructor && Tok.is(tok::tilde))
+ if (CheckForDestructor && !HasScopeSpecifier && Tok.is(tok::tilde))
*MayBePseudoDestructor = true;
return false;
@@ -599,12 +594,12 @@ ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS,
default:
SourceLocation TemplateKWLoc;
UnqualifiedId Name;
- if (ParseUnqualifiedId(SS,
+ if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
/*EnteringContext=*/false,
/*AllowDestructorName=*/false,
/*AllowConstructorName=*/false,
- /*AllowDeductionGuide=*/false,
- /*ObjectType=*/nullptr, &TemplateKWLoc, Name))
+ /*AllowDeductionGuide=*/false, &TemplateKWLoc, Name))
return ExprError();
// This is only the direct operand of an & operator if it is not
@@ -672,7 +667,9 @@ ExprResult Parser::ParseCXXIdExpression(bool isAddressOfOperand) {
// '::' unqualified-id
//
CXXScopeSpec SS;
- ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false);
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ /*EnteringContext=*/false);
Token Replacement;
ExprResult Result =
@@ -1261,17 +1258,16 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
};
// FIXME: Consider allowing this as an extension for GCC compatibiblity.
- const bool HasExplicitTemplateParams = Tok.is(tok::less);
- ParseScope TemplateParamScope(this, Scope::TemplateParamScope,
- /*EnteredScope=*/HasExplicitTemplateParams);
- if (HasExplicitTemplateParams) {
- Diag(Tok, getLangOpts().CPlusPlus2a
+ MultiParseScope TemplateParamScope(*this);
+ if (Tok.is(tok::less)) {
+ Diag(Tok, getLangOpts().CPlusPlus20
? diag::warn_cxx17_compat_lambda_template_parameter_list
: diag::ext_lambda_template_parameter_list);
SmallVector<NamedDecl*, 4> TemplateParams;
SourceLocation LAngleLoc, RAngleLoc;
- if (ParseTemplateParameters(CurTemplateDepthTracker.getDepth(),
+ if (ParseTemplateParameters(TemplateParamScope,
+ CurTemplateDepthTracker.getDepth(),
TemplateParams, LAngleLoc, RAngleLoc)) {
Actions.ActOnLambdaError(LambdaBeginLoc, getCurScope());
return ExprError();
@@ -1306,8 +1302,8 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
Actions.RecordParsingTemplateParameterDepth(
CurTemplateDepthTracker.getOriginalDepth());
- ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc);
-
+ ParseParameterDeclarationClause(D.getContext(), Attr, ParamInfo,
+ EllipsisLoc);
// For a generic lambda, each 'auto' within the parameter declaration
// clause creates a template type parameter, so increment the depth.
// If we've parsed any explicit template parameters, then the depth will
@@ -1516,12 +1512,15 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
/// 'reinterpret_cast' '<' type-name '>' '(' expression ')'
/// 'const_cast' '<' type-name '>' '(' expression ')'
///
+/// C++ for OpenCL s2.3.1 adds:
+/// 'addrspace_cast' '<' type-name '>' '(' expression ')'
ExprResult Parser::ParseCXXCasts() {
tok::TokenKind Kind = Tok.getKind();
const char *CastName = nullptr; // For error messages
switch (Kind) {
default: llvm_unreachable("Unknown C++ cast!");
+ case tok::kw_addrspace_cast: CastName = "addrspace_cast"; break;
case tok::kw_const_cast: CastName = "const_cast"; break;
case tok::kw_dynamic_cast: CastName = "dynamic_cast"; break;
case tok::kw_reinterpret_cast: CastName = "reinterpret_cast"; break;
@@ -1694,31 +1693,42 @@ ExprResult Parser::ParseCXXUuidof() {
/// Parse a C++ pseudo-destructor expression after the base,
/// . or -> operator, and nested-name-specifier have already been
-/// parsed.
+/// parsed. We're handling this fragment of the grammar:
///
-/// postfix-expression: [C++ 5.2]
-/// postfix-expression . pseudo-destructor-name
-/// postfix-expression -> pseudo-destructor-name
+/// postfix-expression: [C++2a expr.post]
+/// postfix-expression . template[opt] id-expression
+/// postfix-expression -> template[opt] id-expression
///
-/// pseudo-destructor-name:
-/// ::[opt] nested-name-specifier[opt] type-name :: ~type-name
-/// ::[opt] nested-name-specifier template simple-template-id ::
-/// ~type-name
-/// ::[opt] nested-name-specifier[opt] ~type-name
+/// id-expression:
+/// qualified-id
+/// unqualified-id
///
+/// qualified-id:
+/// nested-name-specifier template[opt] unqualified-id
+///
+/// nested-name-specifier:
+/// type-name ::
+/// decltype-specifier :: FIXME: not implemented, but probably only
+/// allowed in C++ grammar by accident
+/// nested-name-specifier identifier ::
+/// nested-name-specifier template[opt] simple-template-id ::
+/// [...]
+///
+/// unqualified-id:
+/// ~ type-name
+/// ~ decltype-specifier
+/// [...]
+///
+/// ... where the all but the last component of the nested-name-specifier
+/// has already been parsed, and the base expression is not of a non-dependent
+/// class type.
ExprResult
Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc,
tok::TokenKind OpKind,
CXXScopeSpec &SS,
ParsedType ObjectType) {
- // We're parsing either a pseudo-destructor-name or a dependent
- // member access that has the same form as a
- // pseudo-destructor-name. We parse both in the same way and let
- // the action model sort them out.
- //
- // Note that the ::[opt] nested-name-specifier[opt] has already
- // been parsed, and if there was a simple-template-id, it has
- // been coalesced into a template-id annotation token.
+ // If the last component of the (optional) nested-name-specifier is
+ // template[opt] simple-template-id, it has already been annotated.
UnqualifiedId FirstTypeName;
SourceLocation CCLoc;
if (Tok.is(tok::identifier)) {
@@ -1727,14 +1737,16 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc,
assert(Tok.is(tok::coloncolon) &&"ParseOptionalCXXScopeSpecifier fail");
CCLoc = ConsumeToken();
} else if (Tok.is(tok::annot_template_id)) {
- // FIXME: retrieve TemplateKWLoc from template-id annotation and
- // store it in the pseudo-dtor node (to be used when instantiating it).
- FirstTypeName.setTemplateId(
- (TemplateIdAnnotation *)Tok.getAnnotationValue());
+ TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+ // FIXME: Carry on and build an AST representation for tooling.
+ if (TemplateId->isInvalid())
+ return ExprError();
+ FirstTypeName.setTemplateId(TemplateId);
ConsumeAnnotationToken();
assert(Tok.is(tok::coloncolon) &&"ParseOptionalCXXScopeSpecifier fail");
CCLoc = ConsumeToken();
} else {
+ assert(SS.isEmpty() && "missing last component of nested name specifier");
FirstTypeName.setIdentifier(nullptr, SourceLocation());
}
@@ -1742,7 +1754,7 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc,
assert(Tok.is(tok::tilde) && "ParseOptionalCXXScopeSpecifier fail");
SourceLocation TildeLoc = ConsumeToken();
- if (Tok.is(tok::kw_decltype) && !FirstTypeName.isValid() && SS.isEmpty()) {
+ if (Tok.is(tok::kw_decltype) && !FirstTypeName.isValid()) {
DeclSpec DS(AttrFactory);
ParseDecltypeSpecifier(DS);
if (DS.getTypeSpecType() == TST_error)
@@ -1764,11 +1776,17 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc,
// If there is a '<', the second type name is a template-id. Parse
// it as such.
+ //
+ // FIXME: This is not a context in which a '<' is assumed to start a template
+ // argument list. This affects examples such as
+ // void f(auto *p) { p->~X<int>(); }
+ // ... but there's no ambiguity, and nowhere to write 'template' in such an
+ // example, so we accept it anyway.
if (Tok.is(tok::less) &&
- ParseUnqualifiedIdTemplateId(SS, SourceLocation(),
- Name, NameLoc,
- false, ObjectType, SecondTypeName,
- /*AssumeTemplateId=*/true))
+ ParseUnqualifiedIdTemplateId(
+ SS, ObjectType, Base && Base->containsErrors(), SourceLocation(),
+ Name, NameLoc, false, SecondTypeName,
+ /*AssumeTemplateId=*/true))
return ExprError();
return Actions.ActOnPseudoDestructorExpr(getCurScope(), Base, OpLoc, OpKind,
@@ -1862,6 +1880,7 @@ Parser::ParseCXXTypeConstructExpression(const DeclSpec &DS) {
&& "Expected '(' or '{'!");
if (Tok.is(tok::l_brace)) {
+ PreferredType.enterTypeCast(Tok.getLocation(), TypeRep.get());
ExprResult Init = ParseBraceInitializer();
if (Init.isInvalid())
return Init;
@@ -2131,12 +2150,8 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) {
// type-name
case tok::annot_typename: {
- if (getTypeAnnotation(Tok))
- DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, DiagID,
- getTypeAnnotation(Tok), Policy);
- else
- DS.SetTypeSpecError();
-
+ DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, DiagID,
+ getTypeAnnotation(Tok), Policy);
DS.SetRangeEnd(Tok.getAnnotationEndLoc());
ConsumeAnnotationToken();
@@ -2144,6 +2159,19 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) {
return;
}
+ case tok::kw__ExtInt: {
+ ExprResult ER = ParseExtIntegerArgument();
+ if (ER.isInvalid())
+ DS.SetTypeSpecError();
+ else
+ DS.SetExtIntType(Loc, ER.get(), PrevSpec, DiagID, Policy);
+
+ // Do this here because we have already consumed the close paren.
+ DS.SetRangeEnd(PrevTokLocation);
+ DS.Finish(Actions, Policy);
+ return;
+ }
+
// builtin types
case tok::kw_short:
DS.SetTypeSpecWidth(DeclSpec::TSW_short, Loc, PrevSpec, DiagID, Policy);
@@ -2172,6 +2200,9 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) {
case tok::kw___int128:
DS.SetTypeSpecType(DeclSpec::TST_int128, Loc, PrevSpec, DiagID, Policy);
break;
+ case tok::kw___bf16:
+ DS.SetTypeSpecType(DeclSpec::TST_BFloat16, Loc, PrevSpec, DiagID, Policy);
+ break;
case tok::kw_half:
DS.SetTypeSpecType(DeclSpec::TST_half, Loc, PrevSpec, DiagID, Policy);
break;
@@ -2254,6 +2285,12 @@ bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) {
/// \param SS the nested-name-specifier that precedes this template-id, if
/// we're actually parsing a qualified-id.
///
+/// \param ObjectType if this unqualified-id occurs within a member access
+/// expression, the type of the base object whose member is being accessed.
+///
+/// \param ObjectHadErrors this unqualified-id occurs within a member access
+/// expression, indicates whether the original subexpressions had any errors.
+///
/// \param Name for constructor and destructor names, this is the actual
/// identifier that may be a template-name.
///
@@ -2263,9 +2300,6 @@ bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) {
/// \param EnteringContext whether we're entering the scope of the
/// nested-name-specifier.
///
-/// \param ObjectType if this unqualified-id occurs within a member access
-/// expression, the type of the base object whose member is being accessed.
-///
/// \param Id as input, describes the template-name or operator-function-id
/// that precedes the '<'. If template arguments were parsed successfully,
/// will be updated with the template-id.
@@ -2274,14 +2308,10 @@ bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) {
/// refers to a template without performing name lookup to verify.
///
/// \returns true if a parse error occurred, false otherwise.
-bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
- SourceLocation TemplateKWLoc,
- IdentifierInfo *Name,
- SourceLocation NameLoc,
- bool EnteringContext,
- ParsedType ObjectType,
- UnqualifiedId &Id,
- bool AssumeTemplateId) {
+bool Parser::ParseUnqualifiedIdTemplateId(
+ CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors,
+ SourceLocation TemplateKWLoc, IdentifierInfo *Name, SourceLocation NameLoc,
+ bool EnteringContext, UnqualifiedId &Id, bool AssumeTemplateId) {
assert(Tok.is(tok::less) && "Expected '<' to finish parsing a template-id");
TemplateTy Template;
@@ -2293,11 +2323,9 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
if (AssumeTemplateId) {
// We defer the injected-class-name checks until we've found whether
// this template-id is used to form a nested-name-specifier or not.
- TNK = Actions.ActOnDependentTemplateName(
- getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext,
- Template, /*AllowInjectedClassName*/ true);
- if (TNK == TNK_Non_template)
- return true;
+ TNK = Actions.ActOnTemplateName(getCurScope(), SS, TemplateKWLoc, Id,
+ ObjectType, EnteringContext, Template,
+ /*AllowInjectedClassName*/ true);
} else {
bool MemberOfUnknownSpecialization;
TNK = Actions.isTemplateName(getCurScope(), SS,
@@ -2313,28 +2341,32 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
if (TNK == TNK_Non_template && MemberOfUnknownSpecialization &&
ObjectType && isTemplateArgumentList(0) == TPResult::True) {
- // We have something like t->getAs<T>(), where getAs is a
- // member of an unknown specialization. However, this will only
- // parse correctly as a template, so suggest the keyword 'template'
- // before 'getAs' and treat this as a dependent template name.
- std::string Name;
- if (Id.getKind() == UnqualifiedIdKind::IK_Identifier)
- Name = Id.Identifier->getName();
- else {
- Name = "operator ";
- if (Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId)
- Name += getOperatorSpelling(Id.OperatorFunctionId.Operator);
- else
- Name += Id.Identifier->getName();
+ // If we had errors before, ObjectType can be dependent even without any
+ // templates, do not report missing template keyword in that case.
+ if (!ObjectHadErrors) {
+ // We have something like t->getAs<T>(), where getAs is a
+ // member of an unknown specialization. However, this will only
+ // parse correctly as a template, so suggest the keyword 'template'
+ // before 'getAs' and treat this as a dependent template name.
+ std::string Name;
+ if (Id.getKind() == UnqualifiedIdKind::IK_Identifier)
+ Name = std::string(Id.Identifier->getName());
+ else {
+ Name = "operator ";
+ if (Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId)
+ Name += getOperatorSpelling(Id.OperatorFunctionId.Operator);
+ else
+ Name += Id.Identifier->getName();
+ }
+ Diag(Id.StartLocation, diag::err_missing_dependent_template_keyword)
+ << Name
+ << FixItHint::CreateInsertion(Id.StartLocation, "template ");
}
- Diag(Id.StartLocation, diag::err_missing_dependent_template_keyword)
- << Name
- << FixItHint::CreateInsertion(Id.StartLocation, "template ");
- TNK = Actions.ActOnDependentTemplateName(
+ TNK = Actions.ActOnTemplateName(
getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext,
Template, /*AllowInjectedClassName*/ true);
- if (TNK == TNK_Non_template)
- return true;
+ } else if (TNK == TNK_Non_template) {
+ return false;
}
}
break;
@@ -2347,6 +2379,8 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
TemplateName, ObjectType,
EnteringContext, Template,
MemberOfUnknownSpecialization);
+ if (TNK == TNK_Non_template)
+ return false;
break;
}
@@ -2355,11 +2389,9 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
bool MemberOfUnknownSpecialization;
TemplateName.setIdentifier(Name, NameLoc);
if (ObjectType) {
- TNK = Actions.ActOnDependentTemplateName(
+ TNK = Actions.ActOnTemplateName(
getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType,
EnteringContext, Template, /*AllowInjectedClassName*/ true);
- if (TNK == TNK_Non_template)
- return true;
} else {
TNK = Actions.isTemplateName(getCurScope(), SS, TemplateKWLoc.isValid(),
TemplateName, ObjectType,
@@ -2369,7 +2401,7 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
if (TNK == TNK_Non_template && !Id.DestructorName.get()) {
Diag(NameLoc, diag::err_destructor_template_id)
<< Name << SS.getRange();
- return true;
+ // Carry on to parse the template arguments before bailing out.
}
}
break;
@@ -2379,9 +2411,6 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
return false;
}
- if (TNK == TNK_Non_template)
- return false;
-
// Parse the enclosed template argument list.
SourceLocation LAngleLoc, RAngleLoc;
TemplateArgList TemplateArgs;
@@ -2389,6 +2418,10 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
RAngleLoc))
return true;
+ // If this is a non-template, we already issued a diagnostic.
+ if (TNK == TNK_Non_template)
+ return true;
+
if (Id.getKind() == UnqualifiedIdKind::IK_Identifier ||
Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId ||
Id.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId) {
@@ -2405,8 +2438,8 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
: Id.OperatorFunctionId.Operator;
TemplateIdAnnotation *TemplateId = TemplateIdAnnotation::Create(
- SS, TemplateKWLoc, Id.StartLocation, TemplateII, OpKind, Template, TNK,
- LAngleLoc, RAngleLoc, TemplateArgs, TemplateIds);
+ TemplateKWLoc, Id.StartLocation, TemplateII, OpKind, Template, TNK,
+ LAngleLoc, RAngleLoc, TemplateArgs, /*ArgsInvalid*/false, TemplateIds);
Id.setTemplateId(TemplateId);
return false;
@@ -2686,6 +2719,13 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
/// \param SS The nested-name-specifier that preceded this unqualified-id. If
/// non-empty, then we are parsing the unqualified-id of a qualified-id.
///
+/// \param ObjectType if this unqualified-id occurs within a member access
+/// expression, the type of the base object whose member is being accessed.
+///
+/// \param ObjectHadErrors if this unqualified-id occurs within a member access
+/// expression, indicates whether the original subexpressions had any errors.
+/// When true, diagnostics for missing 'template' keyword will be supressed.
+///
/// \param EnteringContext whether we are entering the scope of the
/// nested-name-specifier.
///
@@ -2695,17 +2735,14 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
///
/// \param AllowDeductionGuide whether we allow parsing a deduction guide name.
///
-/// \param ObjectType if this unqualified-id occurs within a member access
-/// expression, the type of the base object whose member is being accessed.
-///
/// \param Result on a successful parse, contains the parsed unqualified-id.
///
/// \returns true if parsing fails, false otherwise.
-bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
+bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
+ bool ObjectHadErrors, bool EnteringContext,
bool AllowDestructorName,
bool AllowConstructorName,
bool AllowDeductionGuide,
- ParsedType ObjectType,
SourceLocation *TemplateKWLoc,
UnqualifiedId &Result) {
if (TemplateKWLoc)
@@ -2764,10 +2801,11 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
TemplateTy Template;
if (Tok.is(tok::less))
return ParseUnqualifiedIdTemplateId(
- SS, TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), Id, IdLoc,
- EnteringContext, ObjectType, Result, TemplateSpecified);
+ SS, ObjectType, ObjectHadErrors,
+ TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), Id, IdLoc,
+ EnteringContext, Result, TemplateSpecified);
else if (TemplateSpecified &&
- Actions.ActOnDependentTemplateName(
+ Actions.ActOnTemplateName(
getCurScope(), SS, *TemplateKWLoc, Result, ObjectType,
EnteringContext, Template,
/*AllowInjectedClassName*/ true) == TNK_Non_template)
@@ -2781,6 +2819,13 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
if (Tok.is(tok::annot_template_id)) {
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+ // FIXME: Consider passing invalid template-ids on to callers; they may
+ // be able to recover better than we can.
+ if (TemplateId->isInvalid()) {
+ ConsumeAnnotationToken();
+ return true;
+ }
+
// If the template-name names the current class, then this is a constructor
if (AllowConstructorName && TemplateId->Name &&
Actions.isCurrentClassName(*TemplateId->Name, getCurScope(), &SS)) {
@@ -2842,11 +2887,11 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
Result.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId) &&
Tok.is(tok::less))
return ParseUnqualifiedIdTemplateId(
- SS, TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), nullptr,
- SourceLocation(), EnteringContext, ObjectType, Result,
- TemplateSpecified);
+ SS, ObjectType, ObjectHadErrors,
+ TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), nullptr,
+ SourceLocation(), EnteringContext, Result, TemplateSpecified);
else if (TemplateSpecified &&
- Actions.ActOnDependentTemplateName(
+ Actions.ActOnTemplateName(
getCurScope(), SS, *TemplateKWLoc, Result, ObjectType,
EnteringContext, Template,
/*AllowInjectedClassName*/ true) == TNK_Non_template)
@@ -2865,6 +2910,22 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
// Parse the '~'.
SourceLocation TildeLoc = ConsumeToken();
+ if (TemplateSpecified) {
+ // C++ [temp.names]p3:
+ // A name prefixed by the keyword template shall be a template-id [...]
+ //
+ // A template-id cannot begin with a '~' token. This would never work
+ // anyway: x.~A<int>() would specify that the destructor is a template,
+ // not that 'A' is a template.
+ //
+ // FIXME: Suggest replacing the attempted destructor name with a correct
+ // destructor name and recover. (This is not trivial if this would become
+ // a pseudo-destructor name).
+ Diag(*TemplateKWLoc, diag::err_unexpected_template_in_destructor_name)
+ << Tok.getLocation();
+ return true;
+ }
+
if (SS.isEmpty() && Tok.is(tok::kw_decltype)) {
DeclSpec DS(AttrFactory);
SourceLocation EndLoc = ParseDecltypeSpecifier(DS);
@@ -2884,7 +2945,7 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
// If the user wrote ~T::T, correct it to T::~T.
DeclaratorScopeObj DeclScopeObj(*this, SS);
- if (!TemplateSpecified && NextToken().is(tok::coloncolon)) {
+ if (NextToken().is(tok::coloncolon)) {
// Don't let ParseOptionalCXXScopeSpecifier() "correct"
// `int A; struct { ~A::A(); };` to `int A; struct { ~A:A(); };`,
// it will confuse this recovery logic.
@@ -2894,7 +2955,8 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
AnnotateScopeToken(SS, /*NewAnnotation*/true);
SS.clear();
}
- if (ParseOptionalCXXScopeSpecifier(SS, ObjectType, EnteringContext))
+ if (ParseOptionalCXXScopeSpecifier(SS, ObjectType, ObjectHadErrors,
+ EnteringContext))
return true;
if (SS.isNotEmpty())
ObjectType = nullptr;
@@ -2921,8 +2983,9 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext,
if (Tok.is(tok::less)) {
Result.setDestructorName(TildeLoc, nullptr, ClassNameLoc);
return ParseUnqualifiedIdTemplateId(
- SS, TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), ClassName,
- ClassNameLoc, EnteringContext, ObjectType, Result, TemplateSpecified);
+ SS, ObjectType, ObjectHadErrors,
+ TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), ClassName,
+ ClassNameLoc, EnteringContext, Result, TemplateSpecified);
}
// Note that this is a destructor name.
@@ -3057,10 +3120,14 @@ Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) {
auto RunSignatureHelp = [&]() {
ParsedType TypeRep =
Actions.ActOnTypeName(getCurScope(), DeclaratorInfo).get();
- assert(TypeRep && "invalid types should be handled before");
- QualType PreferredType = Actions.ProduceConstructorSignatureHelp(
- getCurScope(), TypeRep.get()->getCanonicalTypeInternal(),
- DeclaratorInfo.getEndLoc(), ConstructorArgs, ConstructorLParen);
+ QualType PreferredType;
+ // ActOnTypeName might adjust DeclaratorInfo and return a null type even
+ // the passing DeclaratorInfo is valid, e.g. running SignatureHelp on
+ // `new decltype(invalid) (^)`.
+ if (TypeRep)
+ PreferredType = Actions.ProduceConstructorSignatureHelp(
+ getCurScope(), TypeRep.get()->getCanonicalTypeInternal(),
+ DeclaratorInfo.getEndLoc(), ConstructorArgs, ConstructorLParen);
CalledSignatureHelp = true;
return PreferredType;
};
@@ -3262,6 +3329,310 @@ Parser::ParseCXXDeleteExpression(bool UseGlobal, SourceLocation Start) {
return Actions.ActOnCXXDelete(Start, UseGlobal, ArrayDelete, Operand.get());
}
+/// ParseRequiresExpression - Parse a C++2a requires-expression.
+/// C++2a [expr.prim.req]p1
+/// A requires-expression provides a concise way to express requirements on
+/// template arguments. A requirement is one that can be checked by name
+/// lookup (6.4) or by checking properties of types and expressions.
+///
+/// requires-expression:
+/// 'requires' requirement-parameter-list[opt] requirement-body
+///
+/// requirement-parameter-list:
+/// '(' parameter-declaration-clause[opt] ')'
+///
+/// requirement-body:
+/// '{' requirement-seq '}'
+///
+/// requirement-seq:
+/// requirement
+/// requirement-seq requirement
+///
+/// requirement:
+/// simple-requirement
+/// type-requirement
+/// compound-requirement
+/// nested-requirement
+ExprResult Parser::ParseRequiresExpression() {
+ assert(Tok.is(tok::kw_requires) && "Expected 'requires' keyword");
+ SourceLocation RequiresKWLoc = ConsumeToken(); // Consume 'requires'
+
+ llvm::SmallVector<ParmVarDecl *, 2> LocalParameterDecls;
+ if (Tok.is(tok::l_paren)) {
+ // requirement parameter list is present.
+ ParseScope LocalParametersScope(this, Scope::FunctionPrototypeScope |
+ Scope::DeclScope);
+ BalancedDelimiterTracker Parens(*this, tok::l_paren);
+ Parens.consumeOpen();
+ if (!Tok.is(tok::r_paren)) {
+ ParsedAttributes FirstArgAttrs(getAttrFactory());
+ SourceLocation EllipsisLoc;
+ llvm::SmallVector<DeclaratorChunk::ParamInfo, 2> LocalParameters;
+ ParseParameterDeclarationClause(DeclaratorContext::RequiresExprContext,
+ FirstArgAttrs, LocalParameters,
+ EllipsisLoc);
+ if (EllipsisLoc.isValid())
+ Diag(EllipsisLoc, diag::err_requires_expr_parameter_list_ellipsis);
+ for (auto &ParamInfo : LocalParameters)
+ LocalParameterDecls.push_back(cast<ParmVarDecl>(ParamInfo.Param));
+ }
+ Parens.consumeClose();
+ }
+
+ BalancedDelimiterTracker Braces(*this, tok::l_brace);
+ if (Braces.expectAndConsume())
+ return ExprError();
+
+ // Start of requirement list
+ llvm::SmallVector<concepts::Requirement *, 2> Requirements;
+
+ // C++2a [expr.prim.req]p2
+ // Expressions appearing within a requirement-body are unevaluated operands.
+ EnterExpressionEvaluationContext Ctx(
+ Actions, Sema::ExpressionEvaluationContext::Unevaluated);
+
+ ParseScope BodyScope(this, Scope::DeclScope);
+ RequiresExprBodyDecl *Body = Actions.ActOnStartRequiresExpr(
+ RequiresKWLoc, LocalParameterDecls, getCurScope());
+
+ if (Tok.is(tok::r_brace)) {
+ // Grammar does not allow an empty body.
+ // requirement-body:
+ // { requirement-seq }
+ // requirement-seq:
+ // requirement
+ // requirement-seq requirement
+ Diag(Tok, diag::err_empty_requires_expr);
+ // Continue anyway and produce a requires expr with no requirements.
+ } else {
+ while (!Tok.is(tok::r_brace)) {
+ switch (Tok.getKind()) {
+ case tok::l_brace: {
+ // Compound requirement
+ // C++ [expr.prim.req.compound]
+ // compound-requirement:
+ // '{' expression '}' 'noexcept'[opt]
+ // return-type-requirement[opt] ';'
+ // return-type-requirement:
+ // trailing-return-type
+ // '->' cv-qualifier-seq[opt] constrained-parameter
+ // cv-qualifier-seq[opt] abstract-declarator[opt]
+ BalancedDelimiterTracker ExprBraces(*this, tok::l_brace);
+ ExprBraces.consumeOpen();
+ ExprResult Expression =
+ Actions.CorrectDelayedTyposInExpr(ParseExpression());
+ if (!Expression.isUsable()) {
+ ExprBraces.skipToEnd();
+ SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ if (ExprBraces.consumeClose())
+ ExprBraces.skipToEnd();
+
+ concepts::Requirement *Req = nullptr;
+ SourceLocation NoexceptLoc;
+ TryConsumeToken(tok::kw_noexcept, NoexceptLoc);
+ if (Tok.is(tok::semi)) {
+ Req = Actions.ActOnCompoundRequirement(Expression.get(), NoexceptLoc);
+ if (Req)
+ Requirements.push_back(Req);
+ break;
+ }
+ if (!TryConsumeToken(tok::arrow))
+ // User probably forgot the arrow, remind them and try to continue.
+ Diag(Tok, diag::err_requires_expr_missing_arrow)
+ << FixItHint::CreateInsertion(Tok.getLocation(), "->");
+ // Try to parse a 'type-constraint'
+ if (TryAnnotateTypeConstraint()) {
+ SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ if (!isTypeConstraintAnnotation()) {
+ Diag(Tok, diag::err_requires_expr_expected_type_constraint);
+ SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ 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),
+ TemplateParameterDepth);
+ ConsumeAnnotationToken();
+ if (Req)
+ Requirements.push_back(Req);
+ break;
+ }
+ default: {
+ bool PossibleRequiresExprInSimpleRequirement = false;
+ if (Tok.is(tok::kw_requires)) {
+ auto IsNestedRequirement = [&] {
+ RevertingTentativeParsingAction TPA(*this);
+ ConsumeToken(); // 'requires'
+ if (Tok.is(tok::l_brace))
+ // This is a requires expression
+ // requires (T t) {
+ // requires { t++; };
+ // ... ^
+ // }
+ return false;
+ if (Tok.is(tok::l_paren)) {
+ // This might be the parameter list of a requires expression
+ ConsumeParen();
+ auto Res = TryParseParameterDeclarationClause();
+ if (Res != TPResult::False) {
+ // Skip to the closing parenthesis
+ // FIXME: Don't traverse these tokens twice (here and in
+ // TryParseParameterDeclarationClause).
+ unsigned Depth = 1;
+ while (Depth != 0) {
+ if (Tok.is(tok::l_paren))
+ Depth++;
+ else if (Tok.is(tok::r_paren))
+ Depth--;
+ ConsumeAnyToken();
+ }
+ // requires (T t) {
+ // requires () ?
+ // ... ^
+ // - OR -
+ // requires (int x) ?
+ // ... ^
+ // }
+ if (Tok.is(tok::l_brace))
+ // requires (...) {
+ // ^ - a requires expression as a
+ // simple-requirement.
+ return false;
+ }
+ }
+ return true;
+ };
+ if (IsNestedRequirement()) {
+ ConsumeToken();
+ // Nested requirement
+ // C++ [expr.prim.req.nested]
+ // nested-requirement:
+ // 'requires' constraint-expression ';'
+ ExprResult ConstraintExpr =
+ Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression());
+ if (ConstraintExpr.isInvalid() || !ConstraintExpr.isUsable()) {
+ SkipUntil(tok::semi, tok::r_brace,
+ SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ if (auto *Req =
+ Actions.ActOnNestedRequirement(ConstraintExpr.get()))
+ Requirements.push_back(Req);
+ else {
+ SkipUntil(tok::semi, tok::r_brace,
+ SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ break;
+ } else
+ PossibleRequiresExprInSimpleRequirement = true;
+ } else if (Tok.is(tok::kw_typename)) {
+ // This might be 'typename T::value_type;' (a type requirement) or
+ // 'typename T::value_type{};' (a simple requirement).
+ TentativeParsingAction TPA(*this);
+
+ // We need to consume the typename to allow 'requires { typename a; }'
+ SourceLocation TypenameKWLoc = ConsumeToken();
+ if (TryAnnotateCXXScopeToken()) {
+ TPA.Commit();
+ SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ CXXScopeSpec SS;
+ if (Tok.is(tok::annot_cxxscope)) {
+ Actions.RestoreNestedNameSpecifierAnnotation(
+ Tok.getAnnotationValue(), Tok.getAnnotationRange(), SS);
+ ConsumeAnnotationToken();
+ }
+
+ if (Tok.isOneOf(tok::identifier, tok::annot_template_id) &&
+ !NextToken().isOneOf(tok::l_brace, tok::l_paren)) {
+ TPA.Commit();
+ SourceLocation NameLoc = Tok.getLocation();
+ IdentifierInfo *II = nullptr;
+ TemplateIdAnnotation *TemplateId = nullptr;
+ if (Tok.is(tok::identifier)) {
+ II = Tok.getIdentifierInfo();
+ ConsumeToken();
+ } else {
+ TemplateId = takeTemplateIdAnnotation(Tok);
+ ConsumeAnnotationToken();
+ if (TemplateId->isInvalid())
+ break;
+ }
+
+ if (auto *Req = Actions.ActOnTypeRequirement(TypenameKWLoc, SS,
+ NameLoc, II,
+ TemplateId)) {
+ Requirements.push_back(Req);
+ }
+ break;
+ }
+ TPA.Revert();
+ }
+ // Simple requirement
+ // C++ [expr.prim.req.simple]
+ // simple-requirement:
+ // expression ';'
+ SourceLocation StartLoc = Tok.getLocation();
+ ExprResult Expression =
+ Actions.CorrectDelayedTyposInExpr(ParseExpression());
+ if (!Expression.isUsable()) {
+ SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ if (!Expression.isInvalid() && PossibleRequiresExprInSimpleRequirement)
+ Diag(StartLoc, diag::warn_requires_expr_in_simple_requirement)
+ << FixItHint::CreateInsertion(StartLoc, "requires");
+ if (auto *Req = Actions.ActOnSimpleRequirement(Expression.get()))
+ Requirements.push_back(Req);
+ else {
+ SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ // User may have tried to put some compound requirement stuff here
+ if (Tok.is(tok::kw_noexcept)) {
+ Diag(Tok, diag::err_requires_expr_simple_requirement_noexcept)
+ << FixItHint::CreateInsertion(StartLoc, "{")
+ << FixItHint::CreateInsertion(Tok.getLocation(), "}");
+ SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
+ break;
+ }
+ break;
+ }
+ }
+ if (ExpectAndConsumeSemi(diag::err_expected_semi_requirement)) {
+ SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch);
+ TryConsumeToken(tok::semi);
+ break;
+ }
+ }
+ if (Requirements.empty()) {
+ // Don't emit an empty requires expr here to avoid confusing the user with
+ // other diagnostics quoting an empty requires expression they never
+ // wrote.
+ Braces.consumeClose();
+ Actions.ActOnFinishRequiresExpr();
+ return ExprError();
+ }
+ }
+ Braces.consumeClose();
+ Actions.ActOnFinishRequiresExpr();
+ return Actions.ActOnRequiresExpr(RequiresKWLoc, Body, LocalParameterDecls,
+ Requirements, Braces.getCloseLocation());
+}
+
static TypeTrait TypeTraitFromTokKind(tok::TokenKind kind) {
switch (kind) {
default: llvm_unreachable("Not a known type trait");
@@ -3277,18 +3648,24 @@ case tok::kw_ ## Spelling: return BTT_ ## Name;
}
static ArrayTypeTrait ArrayTypeTraitFromTokKind(tok::TokenKind kind) {
- switch(kind) {
- default: llvm_unreachable("Not a known binary type trait");
- case tok::kw___array_rank: return ATT_ArrayRank;
- case tok::kw___array_extent: return ATT_ArrayExtent;
+ switch (kind) {
+ default:
+ llvm_unreachable("Not a known array type trait");
+#define ARRAY_TYPE_TRAIT(Spelling, Name, Key) \
+ case tok::kw_##Spelling: \
+ return ATT_##Name;
+#include "clang/Basic/TokenKinds.def"
}
}
static ExpressionTrait ExpressionTraitFromTokKind(tok::TokenKind kind) {
- switch(kind) {
- default: llvm_unreachable("Not a known unary expression trait.");
- case tok::kw___is_lvalue_expr: return ET_IsLValueExpr;
- case tok::kw___is_rvalue_expr: return ET_IsRValueExpr;
+ switch (kind) {
+ default:
+ llvm_unreachable("Not a known unary expression trait.");
+#define EXPRESSION_TRAIT(Spelling, Name, Key) \
+ case tok::kw_##Spelling: \
+ return ET_##Name;
+#include "clang/Basic/TokenKinds.def"
}
}