llvm-project/clang/lib/Parse/ParseTemplate.cpp
Ilya Biryukov 85043c1c14
[Clang] Add a builtin that deduplicate types into a pack (#106730)
The new builtin `__builtin_dedup_pack` removes duplicates from list of
types.

The added builtin is special in that they produce an unexpanded pack
in the spirit of P3115R0 proposal.

Produced packs can be used directly in template argument lists and get
immediately expanded as soon as results of the computation are
available.

It allows to easily combine them, e.g.:

```cpp
template <class ...T>
struct Normalize {
  // Note: sort is not included in this PR, it illustrates the idea.
  using result = std::tuple<
    __builtin_sort_pack<
      __builtin_dedup_pack<int, double, T...>...
    >...>;
}
;
```

Limitations:
- only supported in template arguments and bases,
- can only be used inside the templates, even if non-dependent,
- the builtins cannot be assigned to template template parameters.

The actual implementation proceeds as follows:
- When the compiler encounters a `__builtin_dedup_pack` or other
type-producing
  builtin with dependent arguments, it creates a dependent
  `TemplateSpecializationType`.
- During substitution, if the template arguments are non-dependent, we
  will produce: a new type `SubstBuiltinTemplatePackType`, which stores
  an argument pack that needs to be substituted. This type is similar to
  the existing `SubstTemplateParmPack` in that it carries the argument
  pack that needs to be expanded further. The relevant code is shared.
- On top of that, Clang also wraps the resulting type into
  `TemplateSpecializationType`, but this time only as a sugar.
- To actually expand those packs, we collect the produced
  `SubstBuiltinTemplatePackType` inside `CollectUnexpandedPacks`.
  Because we know the size of the produces packs only after the initial
  substitution, places that do the actual expansion will need to have a
  second run over the substituted type to finalize the expansions (in
  this patch we only support this for template arguments, see
  `ExpandTemplateArgument`).

If the expansion are requested in the places we do not currently
support, we will produce an error.

More follow-up work will be needed to fully shape this:
- adding the builtin that sorts types,
- remove the restrictions for expansions,
- implementing P3115R0 (scheduled for C++29, see
  https://github.com/cplusplus/papers/issues/2300).
2025-08-20 18:11:36 +02:00

1639 lines
62 KiB
C++

//===--- ParseTemplate.cpp - Template Parsing -----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements parsing of C++ templates.
//
//===----------------------------------------------------------------------===//
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Basic/DiagnosticParse.h"
#include "clang/Parse/Parser.h"
#include "clang/Parse/RAIIObjectsForParser.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/Scope.h"
using namespace clang;
unsigned Parser::ReenterTemplateScopes(MultiParseScope &S, Decl *D) {
return Actions.ActOnReenterTemplateScope(D, [&] {
S.Enter(Scope::TemplateParamScope);
return Actions.getCurScope();
});
}
Parser::DeclGroupPtrTy
Parser::ParseDeclarationStartingWithTemplate(DeclaratorContext Context,
SourceLocation &DeclEnd,
ParsedAttributes &AccessAttrs) {
ObjCDeclContextSwitch ObjCDC(*this);
if (Tok.is(tok::kw_template) && NextToken().isNot(tok::less)) {
return ParseExplicitInstantiation(Context, SourceLocation(), ConsumeToken(),
DeclEnd, AccessAttrs,
AccessSpecifier::AS_none);
}
return ParseTemplateDeclarationOrSpecialization(Context, DeclEnd, AccessAttrs,
AccessSpecifier::AS_none);
}
Parser::DeclGroupPtrTy Parser::ParseTemplateDeclarationOrSpecialization(
DeclaratorContext Context, SourceLocation &DeclEnd,
ParsedAttributes &AccessAttrs, AccessSpecifier AS) {
assert(Tok.isOneOf(tok::kw_export, tok::kw_template) &&
"Token does not start a template declaration.");
MultiParseScope TemplateParamScopes(*this);
// Tell the action that names should be checked in the context of
// the declaration to come.
ParsingDeclRAIIObject
ParsingTemplateParams(*this, ParsingDeclRAIIObject::NoParent);
// Parse multiple levels of template headers within this template
// parameter scope, e.g.,
//
// template<typename T>
// template<typename U>
// class A<T>::B { ... };
//
// We parse multiple levels non-recursively so that we can build a
// single data structure containing all of the template parameter
// lists to easily differentiate between the case above and:
//
// template<typename T>
// class A {
// template<typename U> class B;
// };
//
// In the first case, the action for declaring A<T>::B receives
// both template parameter lists. In the second case, the action for
// defining A<T>::B receives just the inner template parameter list
// (and retrieves the outer template parameter list from its
// context).
bool isSpecialization = true;
bool LastParamListWasEmpty = false;
TemplateParameterLists ParamLists;
TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
do {
// Consume the 'export', if any.
SourceLocation ExportLoc;
TryConsumeToken(tok::kw_export, ExportLoc);
// Consume the 'template', which should be here.
SourceLocation TemplateLoc;
if (!TryConsumeToken(tok::kw_template, TemplateLoc)) {
Diag(Tok.getLocation(), diag::err_expected_template);
return nullptr;
}
// Parse the '<' template-parameter-list '>'
SourceLocation LAngleLoc, RAngleLoc;
SmallVector<NamedDecl*, 4> TemplateParams;
if (ParseTemplateParameters(TemplateParamScopes,
CurTemplateDepthTracker.getDepth(),
TemplateParams, LAngleLoc, RAngleLoc)) {
// Skip until the semi-colon or a '}'.
SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
TryConsumeToken(tok::semi);
return nullptr;
}
ExprResult OptionalRequiresClauseConstraintER;
if (!TemplateParams.empty()) {
isSpecialization = false;
++CurTemplateDepthTracker;
if (TryConsumeToken(tok::kw_requires)) {
OptionalRequiresClauseConstraintER =
Actions.ActOnRequiresClause(ParseConstraintLogicalOrExpression(
/*IsTrailingRequiresClause=*/false));
if (!OptionalRequiresClauseConstraintER.isUsable()) {
// Skip until the semi-colon or a '}'.
SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
TryConsumeToken(tok::semi);
return nullptr;
}
}
} else {
LastParamListWasEmpty = true;
}
ParamLists.push_back(Actions.ActOnTemplateParameterList(
CurTemplateDepthTracker.getDepth(), ExportLoc, TemplateLoc, LAngleLoc,
TemplateParams, RAngleLoc, OptionalRequiresClauseConstraintER.get()));
} while (Tok.isOneOf(tok::kw_export, tok::kw_template));
ParsedTemplateInfo TemplateInfo(&ParamLists, isSpecialization,
LastParamListWasEmpty);
// Parse the actual template declaration.
if (Tok.is(tok::kw_concept)) {
Decl *ConceptDecl = ParseConceptDefinition(TemplateInfo, DeclEnd);
// We need to explicitly pass ConceptDecl to ParsingDeclRAIIObject, so that
// delayed diagnostics (e.g. warn_deprecated) have a Decl to work with.
ParsingTemplateParams.complete(ConceptDecl);
return Actions.ConvertDeclToDeclGroup(ConceptDecl);
}
return ParseDeclarationAfterTemplate(
Context, TemplateInfo, ParsingTemplateParams, DeclEnd, AccessAttrs, AS);
}
Parser::DeclGroupPtrTy Parser::ParseTemplateDeclarationOrSpecialization(
DeclaratorContext Context, SourceLocation &DeclEnd, AccessSpecifier AS) {
ParsedAttributes AccessAttrs(AttrFactory);
return ParseTemplateDeclarationOrSpecialization(Context, DeclEnd, AccessAttrs,
AS);
}
Parser::DeclGroupPtrTy Parser::ParseDeclarationAfterTemplate(
DeclaratorContext Context, ParsedTemplateInfo &TemplateInfo,
ParsingDeclRAIIObject &DiagsFromTParams, SourceLocation &DeclEnd,
ParsedAttributes &AccessAttrs, AccessSpecifier AS) {
assert(TemplateInfo.Kind != ParsedTemplateKind::NonTemplate &&
"Template information required");
if (Tok.is(tok::kw_static_assert)) {
// A static_assert declaration may not be templated.
Diag(Tok.getLocation(), diag::err_templated_invalid_declaration)
<< TemplateInfo.getSourceRange();
// Parse the static_assert declaration to improve error recovery.
return Actions.ConvertDeclToDeclGroup(
ParseStaticAssertDeclaration(DeclEnd));
}
// We are parsing a member template.
if (Context == DeclaratorContext::Member)
return ParseCXXClassMemberDeclaration(AS, AccessAttrs, TemplateInfo,
&DiagsFromTParams);
ParsedAttributes DeclAttrs(AttrFactory);
ParsedAttributes DeclSpecAttrs(AttrFactory);
// GNU attributes are applied to the declaration specification while the
// standard attributes are applied to the declaration. We parse the two
// attribute sets into different containters so we can apply them during
// the regular parsing process.
while (MaybeParseCXX11Attributes(DeclAttrs) ||
MaybeParseGNUAttributes(DeclSpecAttrs))
;
if (Tok.is(tok::kw_using))
return ParseUsingDirectiveOrDeclaration(Context, TemplateInfo, DeclEnd,
DeclAttrs);
// Parse the declaration specifiers, stealing any diagnostics from
// the template parameters.
ParsingDeclSpec DS(*this, &DiagsFromTParams);
DS.SetRangeStart(DeclSpecAttrs.Range.getBegin());
DS.SetRangeEnd(DeclSpecAttrs.Range.getEnd());
DS.takeAttributesFrom(DeclSpecAttrs);
ParseDeclarationSpecifiers(DS, TemplateInfo, AS,
getDeclSpecContextFromDeclaratorContext(Context));
if (Tok.is(tok::semi)) {
ProhibitAttributes(DeclAttrs);
DeclEnd = ConsumeToken();
RecordDecl *AnonRecord = nullptr;
Decl *Decl = Actions.ParsedFreeStandingDeclSpec(
getCurScope(), AS, DS, ParsedAttributesView::none(),
TemplateInfo.TemplateParams ? *TemplateInfo.TemplateParams
: MultiTemplateParamsArg(),
TemplateInfo.Kind == ParsedTemplateKind::ExplicitInstantiation,
AnonRecord);
Actions.ActOnDefinedDeclarationSpecifier(Decl);
assert(!AnonRecord &&
"Anonymous unions/structs should not be valid with template");
DS.complete(Decl);
return Actions.ConvertDeclToDeclGroup(Decl);
}
if (DS.hasTagDefinition())
Actions.ActOnDefinedDeclarationSpecifier(DS.getRepAsDecl());
// Move the attributes from the prefix into the DS.
if (TemplateInfo.Kind == ParsedTemplateKind::ExplicitInstantiation)
ProhibitAttributes(DeclAttrs);
return ParseDeclGroup(DS, Context, DeclAttrs, TemplateInfo, &DeclEnd);
}
Decl *
Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo,
SourceLocation &DeclEnd) {
assert(TemplateInfo.Kind != ParsedTemplateKind::NonTemplate &&
"Template information required");
assert(Tok.is(tok::kw_concept) &&
"ParseConceptDefinition must be called when at a 'concept' keyword");
ConsumeToken(); // Consume 'concept'
SourceLocation BoolKWLoc;
if (TryConsumeToken(tok::kw_bool, BoolKWLoc))
Diag(Tok.getLocation(), diag::err_concept_legacy_bool_keyword) <<
FixItHint::CreateRemoval(SourceLocation(BoolKWLoc));
DiagnoseAndSkipCXX11Attributes();
CXXScopeSpec SS;
if (ParseOptionalCXXScopeSpecifier(
SS, /*ObjectType=*/nullptr,
/*ObjectHasErrors=*/false, /*EnteringContext=*/false,
/*MayBePseudoDestructor=*/nullptr,
/*IsTypename=*/false, /*LastII=*/nullptr, /*OnlyNamespace=*/true) ||
SS.isInvalid()) {
SkipUntil(tok::semi);
return nullptr;
}
if (SS.isNotEmpty())
Diag(SS.getBeginLoc(),
diag::err_concept_definition_not_identifier);
UnqualifiedId Result;
if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
/*ObjectHadErrors=*/false, /*EnteringContext=*/false,
/*AllowDestructorName=*/false,
/*AllowConstructorName=*/false,
/*AllowDeductionGuide=*/false,
/*TemplateKWLoc=*/nullptr, Result)) {
SkipUntil(tok::semi);
return nullptr;
}
if (Result.getKind() != UnqualifiedIdKind::IK_Identifier) {
Diag(Result.getBeginLoc(), diag::err_concept_definition_not_identifier);
SkipUntil(tok::semi);
return nullptr;
}
const IdentifierInfo *Id = Result.Identifier;
SourceLocation IdLoc = Result.getBeginLoc();
// [C++26][basic.scope.pdecl]/p13
// The locus of a concept-definition is immediately after its concept-name.
ConceptDecl *D = Actions.ActOnStartConceptDefinition(
getCurScope(), *TemplateInfo.TemplateParams, Id, IdLoc);
ParsedAttributes Attrs(AttrFactory);
MaybeParseAttributes(PAKM_GNU | PAKM_CXX11, Attrs);
if (!TryConsumeToken(tok::equal)) {
Diag(Tok.getLocation(), diag::err_expected) << tok::equal;
SkipUntil(tok::semi);
if (D)
D->setInvalidDecl();
return nullptr;
}
ExprResult ConstraintExprResult = ParseConstraintExpression();
if (ConstraintExprResult.isInvalid()) {
SkipUntil(tok::semi);
if (D)
D->setInvalidDecl();
return nullptr;
}
DeclEnd = Tok.getLocation();
ExpectAndConsumeSemi(diag::err_expected_semi_declaration);
Expr *ConstraintExpr = ConstraintExprResult.get();
if (!D)
return nullptr;
return Actions.ActOnFinishConceptDefinition(getCurScope(), D, ConstraintExpr,
Attrs);
}
bool Parser::ParseTemplateParameters(
MultiParseScope &TemplateScopes, unsigned Depth,
SmallVectorImpl<NamedDecl *> &TemplateParams, SourceLocation &LAngleLoc,
SourceLocation &RAngleLoc) {
// Get the template parameter list.
if (!TryConsumeToken(tok::less, LAngleLoc)) {
Diag(Tok.getLocation(), diag::err_expected_less_after) << "template";
return true;
}
// Try to parse the template parameter list.
bool Failed = false;
// FIXME: Missing greatergreatergreater support.
if (!Tok.is(tok::greater) && !Tok.is(tok::greatergreater)) {
TemplateScopes.Enter(Scope::TemplateParamScope);
Failed = ParseTemplateParameterList(Depth, TemplateParams);
}
if (Tok.is(tok::greatergreater)) {
// No diagnostic required here: a template-parameter-list can only be
// followed by a declaration or, for a template template parameter, the
// 'class' keyword. Therefore, the second '>' will be diagnosed later.
// This matters for elegant diagnosis of:
// template<template<typename>> struct S;
Tok.setKind(tok::greater);
RAngleLoc = Tok.getLocation();
Tok.setLocation(Tok.getLocation().getLocWithOffset(1));
} else if (!TryConsumeToken(tok::greater, RAngleLoc) && Failed) {
Diag(Tok.getLocation(), diag::err_expected) << tok::greater;
return true;
}
return false;
}
bool
Parser::ParseTemplateParameterList(const unsigned Depth,
SmallVectorImpl<NamedDecl*> &TemplateParams) {
while (true) {
if (NamedDecl *TmpParam
= ParseTemplateParameter(Depth, TemplateParams.size())) {
TemplateParams.push_back(TmpParam);
} else {
// If we failed to parse a template parameter, skip until we find
// a comma or closing brace.
SkipUntil(tok::comma, tok::greater, tok::greatergreater,
StopAtSemi | StopBeforeMatch);
}
// Did we find a comma or the end of the template parameter list?
if (Tok.is(tok::comma)) {
ConsumeToken();
} else if (Tok.isOneOf(tok::greater, tok::greatergreater)) {
// Don't consume this... that's done by template parser.
break;
} else {
// Somebody probably forgot to close the template. Skip ahead and
// try to get out of the expression. This error is currently
// subsumed by whatever goes on in ParseTemplateParameter.
Diag(Tok.getLocation(), diag::err_expected_comma_greater);
SkipUntil(tok::comma, tok::greater, tok::greatergreater,
StopAtSemi | StopBeforeMatch);
return false;
}
}
return true;
}
Parser::TPResult Parser::isStartOfTemplateTypeParameter() {
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.
switch (NextToken().getKind()) {
case tok::equal:
case tok::comma:
case tok::greater:
case tok::greatergreater:
case tok::ellipsis:
return TPResult::True;
case tok::identifier:
// This may be either a type-parameter or an elaborated-type-specifier.
// We have to look further.
break;
default:
return TPResult::False;
}
switch (GetLookAheadToken(2).getKind()) {
case tok::equal:
case tok::comma:
case tok::greater:
case tok::greatergreater:
return TPResult::True;
default:
return TPResult::False;
}
}
if (TryAnnotateTypeConstraint())
return TPResult::Error;
if (isTypeConstraintAnnotation() &&
// 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.
!GetLookAheadToken(Tok.is(tok::annot_cxxscope) ? 2 : 1)
.isOneOf(tok::kw_auto, tok::kw_decltype))
return TPResult::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))
return TPResult::False;
// C++ [temp.param]p2:
// There is no semantic difference between class and typename in a
// template-parameter. typename followed by an unqualified-id
// names a template type parameter. typename followed by a
// qualified-id denotes the type in a non-type
// parameter-declaration.
Token Next = NextToken();
// If we have an identifier, skip over it.
if (Next.getKind() == tok::identifier)
Next = GetLookAheadToken(2);
switch (Next.getKind()) {
case tok::equal:
case tok::comma:
case tok::greater:
case tok::greatergreater:
case tok::ellipsis:
return TPResult::True;
case tok::kw_typename:
case tok::kw_typedef:
case tok::kw_class:
// These indicate that a comma was missed after a type parameter, not that
// we have found a non-type parameter.
return TPResult::True;
default:
return TPResult::False;
}
}
NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) {
switch (isStartOfTemplateTypeParameter()) {
case TPResult::True:
// 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()),
"typename");
Tok.setKind(tok::kw_typename);
}
return ParseTypeParameter(Depth, Position);
case TPResult::False:
break;
case TPResult::Error: {
// 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, ParsedAttributesView::none(),
DeclaratorContext::TemplateParam);
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;
}
case TPResult::Ambiguous:
llvm_unreachable("template param classification can't be ambiguous");
}
if (Tok.is(tok::kw_template))
return ParseTemplateTemplateParameter(Depth, Position);
// If it's none of the above, then it must be a parameter declaration.
// NOTE: This will pick up errors in the closure of the template parameter
// list (e.g., template < ; Check here to implement >> style closures.
return ParseNonTypeTemplateParameter(Depth, Position);
}
bool Parser::isTypeConstraintAnnotation() {
const Token &T = Tok.is(tok::annot_cxxscope) ? NextToken() : Tok;
if (T.isNot(tok::annot_template_id))
return false;
const auto *ExistingAnnot =
static_cast<TemplateIdAnnotation *>(T.getAnnotationValue());
return ExistingAnnot->Kind == TNK_Concept_template;
}
bool Parser::TryAnnotateTypeConstraint() {
if (!getLangOpts().CPlusPlus20)
return false;
CXXScopeSpec SS;
bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope);
if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
/*ObjectHasErrors=*/false,
/*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))
return true;
if (Tok.is(tok::identifier)) {
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,
/*Disambiguation=*/true);
if (MemberOfUnknownSpecialization || !PossibleConcept ||
TNK != TNK_Concept_template) {
if (SS.isNotEmpty())
AnnotateScopeToken(SS, !WasScopeAnnotation);
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.
if (AnnotateTemplateIdToken(PossibleConcept, TNK, SS,
/*TemplateKWLoc=*/SourceLocation(),
PossibleConceptName,
/*AllowTypeAnnotation=*/false,
/*TypeConstraint=*/true))
return true;
}
if (SS.isNotEmpty())
AnnotateScopeToken(SS, !WasScopeAnnotation);
return false;
}
NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) {
assert((Tok.isOneOf(tok::kw_class, tok::kw_typename) ||
isTypeConstraintAnnotation()) &&
"A type-parameter starts with 'class', 'typename' or a "
"type-constraint");
CXXScopeSpec TypeConstraintSS;
TemplateIdAnnotation *TypeConstraint = nullptr;
bool TypenameKeyword = false;
SourceLocation KeyLoc;
ParseOptionalCXXScopeSpecifier(TypeConstraintSS, /*ObjectType=*/nullptr,
/*ObjectHasErrors=*/false,
/*EnteringContext*/ false);
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 {
assert(TypeConstraintSS.isEmpty() &&
"expected type constraint after scope specifier");
// Consume the 'class' or 'typename' keyword.
TypenameKeyword = Tok.is(tok::kw_typename);
KeyLoc = ConsumeToken();
}
// Grab the ellipsis (if given).
SourceLocation EllipsisLoc;
if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) {
Diag(EllipsisLoc,
getLangOpts().CPlusPlus11
? diag::warn_cxx98_compat_variadic_templates
: diag::ext_variadic_templates);
}
// Grab the template parameter name (if given)
SourceLocation NameLoc = Tok.getLocation();
IdentifierInfo *ParamName = nullptr;
if (Tok.is(tok::identifier)) {
ParamName = Tok.getIdentifierInfo();
ConsumeToken();
} else if (Tok.isOneOf(tok::equal, tok::comma, tok::greater,
tok::greatergreater)) {
// Unnamed template parameter. Don't have to do anything here, just
// don't consume this token.
} else {
Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
return nullptr;
}
// Recover from misplaced ellipsis.
bool AlreadyHasEllipsis = EllipsisLoc.isValid();
if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
DiagnoseMisplacedEllipsis(EllipsisLoc, NameLoc, AlreadyHasEllipsis, true);
// Grab a default argument (if available).
// Per C++0x [basic.scope.pdecl]p9, we parse the default argument before
// we introduce the type parameter into the local scope.
SourceLocation EqualLoc;
ParsedType DefaultArg;
std::optional<DelayTemplateIdDestructionRAII> DontDestructTemplateIds;
if (TryConsumeToken(tok::equal, EqualLoc)) {
// The default argument might contain a lambda declaration; avoid destroying
// parsed template ids at the end of that declaration because they can be
// used in a type constraint later.
DontDestructTemplateIds.emplace(*this, /*DelayTemplateIdDestruction=*/true);
// The default argument may declare template parameters, notably
// if it contains a generic lambda, so we need to increase
// the template depth as these parameters would not be instantiated
// at the current level.
TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
++CurTemplateDepthTracker;
DefaultArg =
ParseTypeName(/*Range=*/nullptr, DeclaratorContext::TemplateTypeArg)
.get();
}
NamedDecl *NewDecl = Actions.ActOnTypeParameter(getCurScope(),
TypenameKeyword, EllipsisLoc,
KeyLoc, ParamName, NameLoc,
Depth, Position, EqualLoc,
DefaultArg,
TypeConstraint != nullptr);
if (TypeConstraint) {
Actions.ActOnTypeConstraint(TypeConstraintSS, TypeConstraint,
cast<TemplateTypeParmDecl>(NewDecl),
EllipsisLoc);
}
return NewDecl;
}
NamedDecl *Parser::ParseTemplateTemplateParameter(unsigned Depth,
unsigned Position) {
assert(Tok.is(tok::kw_template) && "Expected 'template' keyword");
// Handle the template <...> part.
SourceLocation TemplateLoc = ConsumeToken();
SmallVector<NamedDecl*,8> TemplateParams;
SourceLocation LAngleLoc, RAngleLoc;
ExprResult OptionalRequiresClauseConstraintER;
{
MultiParseScope TemplateParmScope(*this);
if (ParseTemplateParameters(TemplateParmScope, Depth + 1, TemplateParams,
LAngleLoc, RAngleLoc)) {
return nullptr;
}
if (TryConsumeToken(tok::kw_requires)) {
OptionalRequiresClauseConstraintER =
Actions.ActOnRequiresClause(ParseConstraintLogicalOrExpression(
/*IsTrailingRequiresClause=*/false));
if (!OptionalRequiresClauseConstraintER.isUsable()) {
SkipUntil(tok::comma, tok::greater, tok::greatergreater,
StopAtSemi | StopBeforeMatch);
return nullptr;
}
}
}
TemplateNameKind Kind = TemplateNameKind::TNK_Non_template;
SourceLocation NameLoc;
IdentifierInfo *ParamName = nullptr;
SourceLocation EllipsisLoc;
bool TypenameKeyword = false;
if (TryConsumeToken(tok::kw_class)) {
Kind = TemplateNameKind::TNK_Type_template;
} else {
// Provide an ExtWarn if the C++1z feature of using 'typename' here is used.
// Generate a meaningful error if the user forgot to put class before the
// identifier, comma, or greater. Provide a fixit if the identifier, comma,
// or greater appear immediately or after 'struct'. In the latter case,
// replace the keyword with 'class'.
bool Replace = Tok.isOneOf(tok::kw_typename, tok::kw_struct);
const Token &Next = Tok.is(tok::kw_struct) ? NextToken() : Tok;
if (Tok.is(tok::kw_typename)) {
TypenameKeyword = true;
Kind = TemplateNameKind::TNK_Type_template;
Diag(Tok.getLocation(),
getLangOpts().CPlusPlus17
? diag::warn_cxx14_compat_template_template_param_typename
: diag::ext_template_template_param_typename)
<< (!getLangOpts().CPlusPlus17
? FixItHint::CreateReplacement(Tok.getLocation(), "class")
: FixItHint());
Kind = TemplateNameKind::TNK_Type_template;
} else if (TryConsumeToken(tok::kw_concept)) {
Kind = TemplateNameKind::TNK_Concept_template;
} else if (TryConsumeToken(tok::kw_auto)) {
Kind = TemplateNameKind::TNK_Var_template;
} else if (Next.isOneOf(tok::identifier, tok::comma, tok::greater,
tok::greatergreater, tok::ellipsis)) {
// Provide a fixit if the identifier, comma,
// or greater appear immediately or after 'struct'. In the latter case,
// replace the keyword with 'class'.
Diag(Tok.getLocation(), diag::err_class_on_template_template_param)
<< getLangOpts().CPlusPlus17
<< (Replace
? FixItHint::CreateReplacement(Tok.getLocation(), "class")
: FixItHint::CreateInsertion(Tok.getLocation(), "class "));
}
if (Replace)
ConsumeToken();
}
if (!getLangOpts().CPlusPlus26 &&
(Kind == TemplateNameKind::TNK_Concept_template ||
Kind == TemplateNameKind::TNK_Var_template)) {
Diag(PrevTokLocation, diag::err_cxx26_template_template_params)
<< (Kind == TemplateNameKind::TNK_Concept_template);
}
// Parse the ellipsis, if given.
if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
Diag(EllipsisLoc,
getLangOpts().CPlusPlus11
? diag::warn_cxx98_compat_variadic_templates
: diag::ext_variadic_templates);
// Get the identifier, if given.
NameLoc = Tok.getLocation();
if (Tok.is(tok::identifier)) {
ParamName = Tok.getIdentifierInfo();
ConsumeToken();
} else if (Tok.isOneOf(tok::equal, tok::comma, tok::greater,
tok::greatergreater)) {
// Unnamed template parameter. Don't have to do anything here, just
// don't consume this token.
} else {
Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
return nullptr;
}
// Recover from misplaced ellipsis.
bool AlreadyHasEllipsis = EllipsisLoc.isValid();
if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
DiagnoseMisplacedEllipsis(EllipsisLoc, NameLoc, AlreadyHasEllipsis, true);
TemplateParameterList *ParamList = Actions.ActOnTemplateParameterList(
Depth, SourceLocation(), TemplateLoc, LAngleLoc, TemplateParams,
RAngleLoc, OptionalRequiresClauseConstraintER.get());
// Grab a default argument (if available).
// Per C++0x [basic.scope.pdecl]p9, we parse the default argument before
// we introduce the template parameter into the local scope.
SourceLocation EqualLoc;
ParsedTemplateArgument DefaultArg;
if (TryConsumeToken(tok::equal, EqualLoc)) {
DefaultArg = ParseTemplateTemplateArgument();
if (DefaultArg.isInvalid()) {
Diag(Tok.getLocation(),
diag::err_default_template_template_parameter_not_template);
SkipUntil(tok::comma, tok::greater, tok::greatergreater,
StopAtSemi | StopBeforeMatch);
}
}
return Actions.ActOnTemplateTemplateParameter(
getCurScope(), TemplateLoc, Kind, TypenameKeyword, ParamList, EllipsisLoc,
ParamName, NameLoc, Depth, Position, EqualLoc, DefaultArg);
}
NamedDecl *
Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) {
// Parse the declaration-specifiers (i.e., the type).
// FIXME: The type should probably be restricted in some way... Not all
// declarators (parts of declarators?) are accepted for parameters.
DeclSpec DS(AttrFactory);
ParsedTemplateInfo TemplateInfo;
ParseDeclarationSpecifiers(DS, TemplateInfo, AS_none,
DeclSpecContext::DSC_template_param);
// Parse this as a typename.
Declarator ParamDecl(DS, ParsedAttributesView::none(),
DeclaratorContext::TemplateParam);
ParseDeclarator(ParamDecl);
if (DS.getTypeSpecType() == DeclSpec::TST_unspecified) {
Diag(Tok.getLocation(), diag::err_expected_template_parameter);
return nullptr;
}
// Recover from misplaced ellipsis.
SourceLocation EllipsisLoc;
if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
DiagnoseMisplacedEllipsisInDeclarator(EllipsisLoc, ParamDecl);
// If there is a default value, parse it.
// Per C++0x [basic.scope.pdecl]p9, we parse the default argument before
// we introduce the template parameter into the local scope.
SourceLocation EqualLoc;
ExprResult DefaultArg;
if (TryConsumeToken(tok::equal, EqualLoc)) {
if (Tok.is(tok::l_paren) && NextToken().is(tok::l_brace)) {
Diag(Tok.getLocation(), diag::err_stmt_expr_in_default_arg) << 1;
SkipUntil(tok::comma, tok::greater, StopAtSemi | StopBeforeMatch);
} else {
// C++ [temp.param]p15:
// When parsing a default template-argument for a non-type
// template-parameter, the first non-nested > is taken as the
// end of the template-parameter-list rather than a greater-than
// operator.
GreaterThanIsOperatorScope G(GreaterThanIsOperator, false);
// The default argument may declare template parameters, notably
// if it contains a generic lambda, so we need to increase
// the template depth as these parameters would not be instantiated
// at the current level.
TemplateParameterDepthRAII CurTemplateDepthTracker(
TemplateParameterDepth);
++CurTemplateDepthTracker;
EnterExpressionEvaluationContext ConstantEvaluated(
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
DefaultArg = Actions.ActOnConstantExpression(ParseInitializer());
if (DefaultArg.isInvalid())
SkipUntil(tok::comma, tok::greater, StopAtSemi | StopBeforeMatch);
}
}
// Create the parameter.
return Actions.ActOnNonTypeTemplateParameter(getCurScope(), ParamDecl,
Depth, Position, EqualLoc,
DefaultArg.get());
}
void Parser::DiagnoseMisplacedEllipsis(SourceLocation EllipsisLoc,
SourceLocation CorrectLoc,
bool AlreadyHasEllipsis,
bool IdentifierHasName) {
FixItHint Insertion;
if (!AlreadyHasEllipsis)
Insertion = FixItHint::CreateInsertion(CorrectLoc, "...");
Diag(EllipsisLoc, diag::err_misplaced_ellipsis_in_declaration)
<< FixItHint::CreateRemoval(EllipsisLoc) << Insertion
<< !IdentifierHasName;
}
void Parser::DiagnoseMisplacedEllipsisInDeclarator(SourceLocation EllipsisLoc,
Declarator &D) {
assert(EllipsisLoc.isValid());
bool AlreadyHasEllipsis = D.getEllipsisLoc().isValid();
if (!AlreadyHasEllipsis)
D.setEllipsisLoc(EllipsisLoc);
DiagnoseMisplacedEllipsis(EllipsisLoc, D.getIdentifierLoc(),
AlreadyHasEllipsis, D.hasName());
}
bool Parser::ParseGreaterThanInTemplateList(SourceLocation LAngleLoc,
SourceLocation &RAngleLoc,
bool ConsumeLastToken,
bool ObjCGenericList) {
// What will be left once we've consumed the '>'.
tok::TokenKind RemainingToken;
const char *ReplacementStr = "> >";
bool MergeWithNextToken = false;
switch (Tok.getKind()) {
default:
Diag(getEndOfPreviousToken(), diag::err_expected) << tok::greater;
Diag(LAngleLoc, diag::note_matching) << tok::less;
return true;
case tok::greater:
// Determine the location of the '>' token. Only consume this token
// if the caller asked us to.
RAngleLoc = Tok.getLocation();
if (ConsumeLastToken)
ConsumeToken();
return false;
case tok::greatergreater:
RemainingToken = tok::greater;
break;
case tok::greatergreatergreater:
RemainingToken = tok::greatergreater;
break;
case tok::greaterequal:
RemainingToken = tok::equal;
ReplacementStr = "> =";
// Join two adjacent '=' tokens into one, for cases like:
// void (*p)() = f<int>;
// return f<int>==p;
if (NextToken().is(tok::equal) &&
areTokensAdjacent(Tok, NextToken())) {
RemainingToken = tok::equalequal;
MergeWithNextToken = true;
}
break;
case tok::greatergreaterequal:
RemainingToken = tok::greaterequal;
break;
}
// This template-id is terminated by a token that starts with a '>'.
// Outside C++11 and Objective-C, this is now error recovery.
//
// C++11 allows this when the token is '>>', and in CUDA + C++11 mode, we
// extend that treatment to also apply to the '>>>' token.
//
// Objective-C allows this in its type parameter / argument lists.
SourceLocation TokBeforeGreaterLoc = PrevTokLocation;
SourceLocation TokLoc = Tok.getLocation();
Token Next = NextToken();
// Whether splitting the current token after the '>' would undesirably result
// in the remaining token pasting with the token after it. This excludes the
// MergeWithNextToken cases, which we've already handled.
bool PreventMergeWithNextToken =
(RemainingToken == tok::greater ||
RemainingToken == tok::greatergreater) &&
(Next.isOneOf(tok::greater, tok::greatergreater,
tok::greatergreatergreater, tok::equal, tok::greaterequal,
tok::greatergreaterequal, tok::equalequal)) &&
areTokensAdjacent(Tok, Next);
// Diagnose this situation as appropriate.
if (!ObjCGenericList) {
// The source range of the replaced token(s).
CharSourceRange ReplacementRange = CharSourceRange::getCharRange(
TokLoc, Lexer::AdvanceToTokenCharacter(TokLoc, 2, PP.getSourceManager(),
getLangOpts()));
// A hint to put a space between the '>>'s. In order to make the hint as
// clear as possible, we include the characters either side of the space in
// the replacement, rather than just inserting a space at SecondCharLoc.
FixItHint Hint1 = FixItHint::CreateReplacement(ReplacementRange,
ReplacementStr);
// A hint to put another space after the token, if it would otherwise be
// lexed differently.
FixItHint Hint2;
if (PreventMergeWithNextToken)
Hint2 = FixItHint::CreateInsertion(Next.getLocation(), " ");
unsigned DiagId = diag::err_two_right_angle_brackets_need_space;
if (getLangOpts().CPlusPlus11 &&
(Tok.is(tok::greatergreater) || Tok.is(tok::greatergreatergreater)))
DiagId = diag::warn_cxx98_compat_two_right_angle_brackets;
else if (Tok.is(tok::greaterequal))
DiagId = diag::err_right_angle_bracket_equal_needs_space;
Diag(TokLoc, DiagId) << Hint1 << Hint2;
}
// Find the "length" of the resulting '>' token. This is not always 1, as it
// can contain escaped newlines.
unsigned GreaterLength = Lexer::getTokenPrefixLength(
TokLoc, 1, PP.getSourceManager(), getLangOpts());
// Annotate the source buffer to indicate that we split the token after the
// '>'. This allows us to properly find the end of, and extract the spelling
// of, the '>' token later.
RAngleLoc = PP.SplitToken(TokLoc, GreaterLength);
// Strip the initial '>' from the token.
bool CachingTokens = PP.IsPreviousCachedToken(Tok);
Token Greater = Tok;
Greater.setLocation(RAngleLoc);
Greater.setKind(tok::greater);
Greater.setLength(GreaterLength);
unsigned OldLength = Tok.getLength();
if (MergeWithNextToken) {
ConsumeToken();
OldLength += Tok.getLength();
}
Tok.setKind(RemainingToken);
Tok.setLength(OldLength - GreaterLength);
// Split the second token if lexing it normally would lex a different token
// (eg, the fifth token in 'A<B>>>' should re-lex as '>', not '>>').
SourceLocation AfterGreaterLoc = TokLoc.getLocWithOffset(GreaterLength);
if (PreventMergeWithNextToken)
AfterGreaterLoc = PP.SplitToken(AfterGreaterLoc, Tok.getLength());
Tok.setLocation(AfterGreaterLoc);
// Update the token cache to match what we just did if necessary.
if (CachingTokens) {
// If the previous cached token is being merged, delete it.
if (MergeWithNextToken)
PP.ReplacePreviousCachedToken({});
if (ConsumeLastToken)
PP.ReplacePreviousCachedToken({Greater, Tok});
else
PP.ReplacePreviousCachedToken({Greater});
}
if (ConsumeLastToken) {
PrevTokLocation = RAngleLoc;
} else {
PrevTokLocation = TokBeforeGreaterLoc;
PP.EnterToken(Tok, /*IsReinject=*/true);
Tok = Greater;
}
return false;
}
bool Parser::ParseTemplateIdAfterTemplateName(bool ConsumeLastToken,
SourceLocation &LAngleLoc,
TemplateArgList &TemplateArgs,
SourceLocation &RAngleLoc,
TemplateTy Template) {
assert(Tok.is(tok::less) && "Must have already parsed the template-name");
// Consume the '<'.
LAngleLoc = ConsumeToken();
// Parse the optional template-argument-list.
bool Invalid = false;
{
GreaterThanIsOperatorScope G(GreaterThanIsOperator, false);
if (!Tok.isOneOf(tok::greater, tok::greatergreater,
tok::greatergreatergreater, tok::greaterequal,
tok::greatergreaterequal))
Invalid = ParseTemplateArgumentList(TemplateArgs, Template, LAngleLoc);
if (Invalid) {
// Try to find the closing '>'.
if (getLangOpts().CPlusPlus11)
SkipUntil(tok::greater, tok::greatergreater,
tok::greatergreatergreater, StopAtSemi | StopBeforeMatch);
else
SkipUntil(tok::greater, StopAtSemi | StopBeforeMatch);
}
}
return ParseGreaterThanInTemplateList(LAngleLoc, RAngleLoc, ConsumeLastToken,
/*ObjCGenericList=*/false) ||
Invalid;
}
bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK,
CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
UnqualifiedId &TemplateName,
bool AllowTypeAnnotation,
bool TypeConstraint) {
assert(getLangOpts().CPlusPlus && "Can only annotate template-ids in C++");
assert((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");
assert((Template || TNK == TNK_Non_template) && "missing template name");
// Consume the template-name.
SourceLocation TemplateNameLoc = TemplateName.getSourceRange().getBegin();
// Parse the enclosed template argument list.
SourceLocation LAngleLoc, RAngleLoc;
TemplateArgList TemplateArgs;
bool ArgsInvalid = false;
if (!TypeConstraint || Tok.is(tok::less)) {
ArgsInvalid = ParseTemplateIdAfterTemplateName(
false, LAngleLoc, TemplateArgs, RAngleLoc, Template);
// If we couldn't recover from invalid arguments, don't form an annotation
// token -- we don't know how much to annotate.
// FIXME: This can lead to duplicate diagnostics if we retry parsing this
// template-id in another context. Try to annotate anyway?
if (RAngleLoc.isInvalid())
return true;
}
ASTTemplateArgsPtr TemplateArgsPtr(TemplateArgs);
// Build the annotation token.
if (TNK == TNK_Type_template && AllowTypeAnnotation) {
TypeResult Type =
ArgsInvalid
? TypeError()
: Actions.ActOnTemplateIdType(
getCurScope(), ElaboratedTypeKeyword::None,
/*ElaboratedKeywordLoc=*/SourceLocation(), SS, TemplateKWLoc,
Template, TemplateName.Identifier, TemplateNameLoc, LAngleLoc,
TemplateArgsPtr, RAngleLoc);
Tok.setKind(tok::annot_typename);
setTypeAnnotation(Tok, Type);
if (SS.isNotEmpty())
Tok.setLocation(SS.getBeginLoc());
else if (TemplateKWLoc.isValid())
Tok.setLocation(TemplateKWLoc);
else
Tok.setLocation(TemplateNameLoc);
} else {
// Build a template-id annotation token that can be processed
// later.
Tok.setKind(tok::annot_template_id);
const IdentifierInfo *TemplateII =
TemplateName.getKind() == UnqualifiedIdKind::IK_Identifier
? TemplateName.Identifier
: nullptr;
OverloadedOperatorKind OpKind =
TemplateName.getKind() == UnqualifiedIdKind::IK_Identifier
? OO_None
: TemplateName.OperatorFunctionId.Operator;
TemplateIdAnnotation *TemplateId = TemplateIdAnnotation::Create(
TemplateKWLoc, TemplateNameLoc, TemplateII, OpKind, Template, TNK,
LAngleLoc, RAngleLoc, TemplateArgs, ArgsInvalid, TemplateIds);
Tok.setAnnotationValue(TemplateId);
if (TemplateKWLoc.isValid())
Tok.setLocation(TemplateKWLoc);
else
Tok.setLocation(TemplateNameLoc);
}
// Common fields for the annotation token
Tok.setAnnotationEndLoc(RAngleLoc);
// In case the tokens were cached, have Preprocessor replace them with the
// annotation token.
PP.AnnotateCachedTokens(Tok);
return false;
}
void Parser::AnnotateTemplateIdTokenAsType(
CXXScopeSpec &SS, ImplicitTypenameContext AllowImplicitTypename,
bool IsClassName) {
assert(Tok.is(tok::annot_template_id) && "Requires template-id tokens");
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
assert(TemplateId->mightBeType() &&
"Only works for type and dependent templates");
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
TemplateId->NumArgs);
TypeResult Type =
TemplateId->isInvalid()
? TypeError()
: Actions.ActOnTemplateIdType(
getCurScope(), ElaboratedTypeKeyword::None,
/*ElaboratedKeywordLoc=*/SourceLocation(), SS,
TemplateId->TemplateKWLoc, TemplateId->Template,
TemplateId->Name, TemplateId->TemplateNameLoc,
TemplateId->LAngleLoc, TemplateArgsPtr, TemplateId->RAngleLoc,
/*IsCtorOrDtorName=*/false, IsClassName, AllowImplicitTypename);
// Create the new "type" annotation token.
Tok.setKind(tok::annot_typename);
setTypeAnnotation(Tok, Type);
if (SS.isNotEmpty()) // it was a C++ qualified type name.
Tok.setLocation(SS.getBeginLoc());
// End location stays the same
// Replace the template-id annotation token, and possible the scope-specifier
// that precedes it, with the typename annotation token.
PP.AnnotateCachedTokens(Tok);
}
/// Determine whether the given token can end a template argument.
static bool isEndOfTemplateArgument(Token Tok) {
// FIXME: Handle '>>>'.
return Tok.isOneOf(tok::comma, tok::greater, tok::greatergreater,
tok::greatergreatergreater);
}
ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() {
if (!Tok.is(tok::identifier) && !Tok.is(tok::coloncolon) &&
!Tok.is(tok::annot_cxxscope) && !Tok.is(tok::annot_template_id) &&
!Tok.is(tok::annot_non_type))
return ParsedTemplateArgument();
// C++0x [temp.arg.template]p1:
// A template-argument for a template template-parameter shall be the name
// of a class template or an alias template, expressed as id-expression.
//
// We parse an id-expression that refers to a class template or alias
// template. The grammar we parse is:
//
// nested-name-specifier[opt] template[opt] identifier ...[opt]
//
// followed by a token that terminates a template argument, such as ',',
// '>', or (in some cases) '>>'.
CXXScopeSpec SS; // nested-name-specifier, if present
ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
/*ObjectHasErrors=*/false,
/*EnteringContext=*/false);
ParsedTemplateArgument Result;
SourceLocation EllipsisLoc;
if (SS.isSet() && Tok.is(tok::kw_template)) {
// Parse the optional 'template' keyword following the
// nested-name-specifier.
SourceLocation TemplateKWLoc = ConsumeToken();
if (Tok.is(tok::identifier)) {
// We appear to have a dependent template name.
UnqualifiedId Name;
Name.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
ConsumeToken(); // the identifier
TryConsumeToken(tok::ellipsis, EllipsisLoc);
// If the next token signals the end of a template argument, then we have
// a (possibly-dependent) template name that could be a template template
// argument.
TemplateTy Template;
if (isEndOfTemplateArgument(Tok) &&
Actions.ActOnTemplateName(getCurScope(), SS, TemplateKWLoc, Name,
/*ObjectType=*/nullptr,
/*EnteringContext=*/false, Template))
Result = ParsedTemplateArgument(TemplateKWLoc, SS, Template,
Name.StartLocation);
}
} else if (Tok.is(tok::identifier) || Tok.is(tok::annot_template_id) ||
Tok.is(tok::annot_non_type)) {
// We may have a (non-dependent) template name.
TemplateTy Template;
UnqualifiedId Name;
if (Tok.is(tok::annot_non_type)) {
NamedDecl *ND = getNonTypeAnnotation(Tok);
if (!isa<VarTemplateDecl>(ND))
return Result;
Name.setIdentifier(ND->getIdentifier(), Tok.getLocation());
ConsumeAnnotationToken();
} else if (Tok.is(tok::annot_template_id)) {
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
if (TemplateId->LAngleLoc.isValid())
return Result;
Name.setIdentifier(TemplateId->Name, Tok.getLocation());
ConsumeAnnotationToken();
} else {
Name.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
ConsumeToken(); // the identifier
}
TryConsumeToken(tok::ellipsis, EllipsisLoc);
if (isEndOfTemplateArgument(Tok)) {
bool MemberOfUnknownSpecialization;
TemplateNameKind TNK = Actions.isTemplateName(
getCurScope(), SS,
/*hasTemplateKeyword=*/false, Name,
/*ObjectType=*/nullptr,
/*EnteringContext=*/false, Template, MemberOfUnknownSpecialization);
if (TNK == TNK_Dependent_template_name || TNK == TNK_Type_template ||
TNK == TNK_Var_template || TNK == TNK_Concept_template) {
// We have an id-expression that refers to a class template or
// (C++0x) alias template.
Result = ParsedTemplateArgument(/*TemplateKwLoc=*/SourceLocation(), SS,
Template, Name.StartLocation);
}
}
}
Result = Actions.ActOnTemplateTemplateArgument(Result);
// If this is a pack expansion, build it as such.
if (EllipsisLoc.isValid() && !Result.isInvalid())
Result = Actions.ActOnPackExpansion(Result, EllipsisLoc);
return Result;
}
ParsedTemplateArgument Parser::ParseTemplateArgument() {
// C++ [temp.arg]p2:
// In a template-argument, an ambiguity between a type-id and an
// expression is resolved to a type-id, regardless of the form of
// the corresponding template-parameter.
//
// Therefore, we initially try to parse a type-id - and isCXXTypeId might look
// up and annotate an identifier as an id-expression during disambiguation,
// so enter the appropriate context for a constant expression template
// argument before trying to disambiguate.
EnterExpressionEvaluationContext EnterConstantEvaluated(
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated,
/*LambdaContextDecl=*/nullptr,
/*ExprContext=*/Sema::ExpressionEvaluationContextRecord::EK_TemplateArgument);
if (isCXXTypeId(TentativeCXXTypeIdContext::AsTemplateArgument)) {
TypeResult TypeArg = ParseTypeName(
/*Range=*/nullptr, DeclaratorContext::TemplateArg);
return Actions.ActOnTemplateTypeArgument(TypeArg);
}
// Try to parse a template template argument.
{
TentativeParsingAction TPA(*this);
ParsedTemplateArgument TemplateTemplateArgument =
ParseTemplateTemplateArgument();
if (!TemplateTemplateArgument.isInvalid()) {
TPA.Commit();
return TemplateTemplateArgument;
}
// Revert this tentative parse to parse a non-type template argument.
TPA.Revert();
}
// Parse a non-type template argument.
ExprResult ExprArg;
SourceLocation Loc = Tok.getLocation();
if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace))
ExprArg = ParseBraceInitializer();
else
ExprArg = ParseConstantExpressionInExprEvalContext(
TypoCorrectionTypeBehavior::AllowBoth);
if (ExprArg.isInvalid() || !ExprArg.get()) {
return ParsedTemplateArgument();
}
return ParsedTemplateArgument(ParsedTemplateArgument::NonType,
ExprArg.get(), Loc);
}
bool Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs,
TemplateTy Template,
SourceLocation OpenLoc) {
ColonProtectionRAIIObject ColonProtection(*this, false);
auto RunSignatureHelp = [&] {
if (!Template)
return QualType();
CalledSignatureHelp = true;
return Actions.CodeCompletion().ProduceTemplateArgumentSignatureHelp(
Template, TemplateArgs, OpenLoc);
};
do {
PreferredType.enterFunctionArgument(Tok.getLocation(), RunSignatureHelp);
ParsedTemplateArgument Arg = ParseTemplateArgument();
SourceLocation EllipsisLoc;
if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
Arg = Actions.ActOnPackExpansion(Arg, EllipsisLoc);
if (Arg.isInvalid()) {
if (PP.isCodeCompletionReached() && !CalledSignatureHelp)
RunSignatureHelp();
return true;
}
// Save this template argument.
TemplateArgs.push_back(Arg);
// If the next token is a comma, consume it and keep reading
// arguments.
} while (TryConsumeToken(tok::comma));
return false;
}
Parser::DeclGroupPtrTy Parser::ParseExplicitInstantiation(
DeclaratorContext Context, SourceLocation ExternLoc,
SourceLocation TemplateLoc, SourceLocation &DeclEnd,
ParsedAttributes &AccessAttrs, AccessSpecifier AS) {
// This isn't really required here.
ParsingDeclRAIIObject
ParsingTemplateParams(*this, ParsingDeclRAIIObject::NoParent);
ParsedTemplateInfo TemplateInfo(ExternLoc, TemplateLoc);
return ParseDeclarationAfterTemplate(
Context, TemplateInfo, ParsingTemplateParams, DeclEnd, AccessAttrs, AS);
}
SourceRange Parser::ParsedTemplateInfo::getSourceRange() const {
if (TemplateParams)
return getTemplateParamsRange(TemplateParams->data(),
TemplateParams->size());
SourceRange R(TemplateLoc);
if (ExternLoc.isValid())
R.setBegin(ExternLoc);
return R;
}
void Parser::LateTemplateParserCallback(void *P, LateParsedTemplate &LPT) {
((Parser *)P)->ParseLateTemplatedFuncDef(LPT);
}
void Parser::ParseLateTemplatedFuncDef(LateParsedTemplate &LPT) {
if (!LPT.D)
return;
// Destroy TemplateIdAnnotations when we're done, if possible.
DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(*this);
// Get the FunctionDecl.
FunctionDecl *FunD = LPT.D->getAsFunction();
// Track template parameter depth.
TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
// To restore the context after late parsing.
Sema::ContextRAII GlobalSavedContext(
Actions, Actions.Context.getTranslationUnitDecl());
MultiParseScope Scopes(*this);
// Get the list of DeclContexts to reenter.
SmallVector<DeclContext*, 4> DeclContextsToReenter;
for (DeclContext *DC = FunD; DC && !DC->isTranslationUnit();
DC = DC->getLexicalParent())
DeclContextsToReenter.push_back(DC);
// Reenter scopes from outermost to innermost.
for (DeclContext *DC : reverse(DeclContextsToReenter)) {
CurTemplateDepthTracker.addDepth(
ReenterTemplateScopes(Scopes, cast<Decl>(DC)));
Scopes.Enter(Scope::DeclScope);
// We'll reenter the function context itself below.
if (DC != FunD)
Actions.PushDeclContext(Actions.getCurScope(), DC);
}
// Parsing should occur with empty FP pragma stack and FP options used in the
// point of the template definition.
Sema::FpPragmaStackSaveRAII SavedStack(Actions);
Actions.resetFPOptions(LPT.FPO);
assert(!LPT.Toks.empty() && "Empty body!");
// Append the current token at the end of the new token stream so that it
// doesn't get lost.
LPT.Toks.push_back(Tok);
PP.EnterTokenStream(LPT.Toks, true, /*IsReinject*/true);
// Consume the previously pushed token.
ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
assert(Tok.isOneOf(tok::l_brace, tok::colon, tok::kw_try) &&
"Inline method not starting with '{', ':' or 'try'");
// Parse the method body. Function body parsing code is similar enough
// to be re-used for method bodies as well.
ParseScope FnScope(this, Scope::FnScope | Scope::DeclScope |
Scope::CompoundStmtScope);
// Recreate the containing function DeclContext.
Sema::ContextRAII FunctionSavedContext(Actions, FunD->getLexicalParent());
Actions.ActOnStartOfFunctionDef(getCurScope(), FunD);
if (Tok.is(tok::kw_try)) {
ParseFunctionTryBlock(LPT.D, FnScope);
} else {
if (Tok.is(tok::colon))
ParseConstructorInitializer(LPT.D);
else
Actions.ActOnDefaultCtorInitializers(LPT.D);
if (Tok.is(tok::l_brace)) {
assert((!isa<FunctionTemplateDecl>(LPT.D) ||
cast<FunctionTemplateDecl>(LPT.D)
->getTemplateParameters()
->getDepth() == TemplateParameterDepth - 1) &&
"TemplateParameterDepth should be greater than the depth of "
"current template being instantiated!");
ParseFunctionStatementBody(LPT.D, FnScope);
Actions.UnmarkAsLateParsedTemplate(FunD);
} else
Actions.ActOnFinishFunctionBody(LPT.D, nullptr);
}
}
void Parser::LexTemplateFunctionForLateParsing(CachedTokens &Toks) {
tok::TokenKind kind = Tok.getKind();
if (!ConsumeAndStoreFunctionPrologue(Toks)) {
// Consume everything up to (and including) the matching right brace.
ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
}
// If we're in a function-try-block, we need to store all the catch blocks.
if (kind == tok::kw_try) {
while (Tok.is(tok::kw_catch)) {
ConsumeAndStoreUntil(tok::l_brace, Toks, /*StopAtSemi=*/false);
ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false);
}
}
}
bool Parser::diagnoseUnknownTemplateId(ExprResult LHS, SourceLocation Less) {
TentativeParsingAction TPA(*this);
// FIXME: We could look at the token sequence in a lot more detail here.
if (SkipUntil(tok::greater, tok::greatergreater, tok::greatergreatergreater,
StopAtSemi | StopBeforeMatch)) {
TPA.Commit();
SourceLocation Greater;
ParseGreaterThanInTemplateList(Less, Greater, true, false);
Actions.diagnoseExprIntendedAsTemplateName(getCurScope(), LHS,
Less, Greater);
return true;
}
// There's no matching '>' token, this probably isn't supposed to be
// interpreted as a template-id. Parse it as an (ill-formed) comparison.
TPA.Revert();
return false;
}
void Parser::checkPotentialAngleBracket(ExprResult &PotentialTemplateName) {
assert(Tok.is(tok::less) && "not at a potential angle bracket");
bool DependentTemplateName = false;
if (!Actions.mightBeIntendedToBeTemplateName(PotentialTemplateName,
DependentTemplateName))
return;
// OK, this might be a name that the user intended to be parsed as a
// template-name, followed by a '<' token. Check for some easy cases.
// If we have potential_template<>, then it's supposed to be a template-name.
if (NextToken().is(tok::greater) ||
(getLangOpts().CPlusPlus11 &&
NextToken().isOneOf(tok::greatergreater, tok::greatergreatergreater))) {
SourceLocation Less = ConsumeToken();
SourceLocation Greater;
ParseGreaterThanInTemplateList(Less, Greater, true, false);
Actions.diagnoseExprIntendedAsTemplateName(
getCurScope(), PotentialTemplateName, Less, Greater);
// FIXME: Perform error recovery.
PotentialTemplateName = ExprError();
return;
}
// If we have 'potential_template<type-id', assume it's supposed to be a
// template-name if there's a matching '>' later on.
{
// FIXME: Avoid the tentative parse when NextToken() can't begin a type.
TentativeParsingAction TPA(*this);
SourceLocation Less = ConsumeToken();
if (isTypeIdUnambiguously() &&
diagnoseUnknownTemplateId(PotentialTemplateName, Less)) {
TPA.Commit();
// FIXME: Perform error recovery.
PotentialTemplateName = ExprError();
return;
}
TPA.Revert();
}
// Otherwise, remember that we saw this in case we see a potentially-matching
// '>' token later on.
AngleBracketTracker::Priority Priority =
(DependentTemplateName ? AngleBracketTracker::DependentName
: AngleBracketTracker::PotentialTypo) |
(Tok.hasLeadingSpace() ? AngleBracketTracker::SpaceBeforeLess
: AngleBracketTracker::NoSpaceBeforeLess);
AngleBrackets.add(*this, PotentialTemplateName.get(), Tok.getLocation(),
Priority);
}
bool Parser::checkPotentialAngleBracketDelimiter(
const AngleBracketTracker::Loc &LAngle, const Token &OpToken) {
// If a comma in an expression context is followed by a type that can be a
// template argument and cannot be an expression, then this is ill-formed,
// but might be intended to be part of a template-id.
if (OpToken.is(tok::comma) && isTypeIdUnambiguously() &&
diagnoseUnknownTemplateId(LAngle.TemplateName, LAngle.LessLoc)) {
AngleBrackets.clear(*this);
return true;
}
// If a context that looks like a template-id is followed by '()', then
// this is ill-formed, but might be intended to be a template-id
// followed by '()'.
if (OpToken.is(tok::greater) && Tok.is(tok::l_paren) &&
NextToken().is(tok::r_paren)) {
Actions.diagnoseExprIntendedAsTemplateName(
getCurScope(), LAngle.TemplateName, LAngle.LessLoc,
OpToken.getLocation());
AngleBrackets.clear(*this);
return true;
}
// After a '>' (etc), we're no longer potentially in a construct that's
// intended to be treated as a template-id.
if (OpToken.is(tok::greater) ||
(getLangOpts().CPlusPlus11 &&
OpToken.isOneOf(tok::greatergreater, tok::greatergreatergreater)))
AngleBrackets.clear(*this);
return false;
}