diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2020-01-17 20:45:01 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2020-01-17 20:45:01 +0000 | 
| commit | 706b4fc47bbc608932d3b491ae19a3b9cde9497b (patch) | |
| tree | 4adf86a776049cbf7f69a1929c4babcbbef925eb /clang/lib/Parse/ParseTemplate.cpp | |
| parent | 7cc9cf2bf09f069cb2dd947ead05d0b54301fb71 (diff) | |
Notes
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);  | 
