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