From: Douglas Gregor Date: Wed, 4 Nov 2009 00:56:37 +0000 (+0000) Subject: Implement support for parsing dependent template-ids that refer to X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ca1bdd7c269a2390d43c040a60511edd017ee130;p=clang Implement support for parsing dependent template-ids that refer to overloaded operators, e.g., p->template operator+() git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@85989 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index a51a3d6167..7392170be9 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -16,6 +16,7 @@ #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/OperatorKinds.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/NestedNameSpecifier.h" @@ -739,6 +740,8 @@ public: TemplateName getDependentTemplateName(NestedNameSpecifier *NNS, const IdentifierInfo *Name); + TemplateName getDependentTemplateName(NestedNameSpecifier *NNS, + OverloadedOperatorKind Operator); enum GetBuiltinTypeError { GE_None, //< No error diff --git a/include/clang/AST/TemplateName.h b/include/clang/AST/TemplateName.h index 66ff34cf1e..8ef8fb5141 100644 --- a/include/clang/AST/TemplateName.h +++ b/include/clang/AST/TemplateName.h @@ -16,6 +16,7 @@ #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/PointerUnion.h" +#include "clang/Basic/OperatorKinds.h" namespace llvm { class raw_ostream; @@ -224,10 +225,24 @@ public: class DependentTemplateName : public llvm::FoldingSetNode { /// \brief The nested name specifier that qualifies the template /// name. - NestedNameSpecifier *Qualifier; + /// + /// The bit stored in this qualifier describes whether the \c Name field + /// is interpreted as an IdentifierInfo pointer (when clear) or as an + /// overloaded operator kind (when set). + llvm::PointerIntPair Qualifier; /// \brief The dependent template name. - const IdentifierInfo *Name; + union { + /// \brief The identifier template name. + /// + /// Only valid when the bit on \c Qualifier is clear. + const IdentifierInfo *Identifier; + + /// \brief The overloaded operator name. + /// + /// Only valid when the bit on \c Qualifier is set. + OverloadedOperatorKind Operator; + }; /// \brief The canonical template name to which this dependent /// template name refers. @@ -240,30 +255,70 @@ class DependentTemplateName : public llvm::FoldingSetNode { friend class ASTContext; DependentTemplateName(NestedNameSpecifier *Qualifier, - const IdentifierInfo *Name) - : Qualifier(Qualifier), Name(Name), CanonicalTemplateName(this) { } + const IdentifierInfo *Identifier) + : Qualifier(Qualifier, false), Identifier(Identifier), + CanonicalTemplateName(this) { } DependentTemplateName(NestedNameSpecifier *Qualifier, - const IdentifierInfo *Name, + const IdentifierInfo *Identifier, TemplateName Canon) - : Qualifier(Qualifier), Name(Name), CanonicalTemplateName(Canon) { } + : Qualifier(Qualifier, false), Identifier(Identifier), + CanonicalTemplateName(Canon) { } + DependentTemplateName(NestedNameSpecifier *Qualifier, + OverloadedOperatorKind Operator) + : Qualifier(Qualifier, true), Operator(Operator), + CanonicalTemplateName(this) { } + + DependentTemplateName(NestedNameSpecifier *Qualifier, + OverloadedOperatorKind Operator, + TemplateName Canon) + : Qualifier(Qualifier, true), Operator(Operator), + CanonicalTemplateName(Canon) { } + public: /// \brief Return the nested name specifier that qualifies this name. - NestedNameSpecifier *getQualifier() const { return Qualifier; } + NestedNameSpecifier *getQualifier() const { return Qualifier.getPointer(); } - /// \brief Return the name to which this dependent template name - /// refers. - const IdentifierInfo *getName() const { return Name; } + /// \brief Determine whether this template name refers to an identifier. + bool isIdentifier() const { return !Qualifier.getInt(); } + /// \brief Returns the identifier to which this template name refers. + const IdentifierInfo *getIdentifier() const { + assert(isIdentifier() && "Template name isn't an identifier?"); + return Identifier; + } + + /// \brief Determine whether this template name refers to an overloaded + /// operator. + bool isOverloadedOperator() const { return Qualifier.getInt(); } + + /// \brief Return the overloaded operator to which this template name refers. + OverloadedOperatorKind getOperator() const { + assert(isOverloadedOperator() && + "Template name isn't an overloaded operator?"); + return Operator; + } + void Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getQualifier(), getName()); + if (isIdentifier()) + Profile(ID, getQualifier(), getIdentifier()); + else + Profile(ID, getQualifier(), getOperator()); + } + + static void Profile(llvm::FoldingSetNodeID &ID, NestedNameSpecifier *NNS, + const IdentifierInfo *Identifier) { + ID.AddPointer(NNS); + ID.AddBoolean(false); + ID.AddPointer(Identifier); } static void Profile(llvm::FoldingSetNodeID &ID, NestedNameSpecifier *NNS, - const IdentifierInfo *Name) { + OverloadedOperatorKind Operator) { ID.AddPointer(NNS); - ID.AddPointer(Name); + ID.AddBoolean(true); + ID.AddInteger(Operator); } }; diff --git a/include/clang/Basic/OperatorKinds.h b/include/clang/Basic/OperatorKinds.h index 790b75ba3a..c0a95051a7 100644 --- a/include/clang/Basic/OperatorKinds.h +++ b/include/clang/Basic/OperatorKinds.h @@ -26,7 +26,10 @@ enum OverloadedOperatorKind { NUM_OVERLOADED_OPERATORS }; - +/// \brief Retrieve the spelling of the given overloaded operator, without +/// the preceding "operator" keyword. +const char *getOperatorSpelling(OverloadedOperatorKind Operator); + } // end namespace clang #endif diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 3c0bfccd0d..eb2ec53e6f 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1218,6 +1218,9 @@ private: bool EnteringContext, TypeTy *ObjectType, UnqualifiedId &Id); + bool ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext, + TypeTy *ObjectType, + UnqualifiedId &Result); bool ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, bool AllowDestructorName, bool AllowConstructorName, @@ -1274,6 +1277,7 @@ private: bool AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK, const CXXScopeSpec *SS, + UnqualifiedId &TemplateName, SourceLocation TemplateKWLoc = SourceLocation(), bool AllowTypeAnnotation = true); void AnnotateTemplateIdTokenAsType(const CXXScopeSpec *SS = 0); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index d445125459..aef3d29894 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3662,6 +3662,36 @@ TemplateName ASTContext::getDependentTemplateName(NestedNameSpecifier *NNS, return TemplateName(QTN); } +/// \brief Retrieve the template name that represents a dependent +/// template name such as \c MetaFun::template operator+. +TemplateName +ASTContext::getDependentTemplateName(NestedNameSpecifier *NNS, + OverloadedOperatorKind Operator) { + assert((!NNS || NNS->isDependent()) && + "Nested name specifier must be dependent"); + + llvm::FoldingSetNodeID ID; + DependentTemplateName::Profile(ID, NNS, Operator); + + void *InsertPos = 0; + DependentTemplateName *QTN = + DependentTemplateNames.FindNodeOrInsertPos(ID, InsertPos); + + if (QTN) + return TemplateName(QTN); + + NestedNameSpecifier *CanonNNS = getCanonicalNestedNameSpecifier(NNS); + if (CanonNNS == NNS) { + QTN = new (*this,4) DependentTemplateName(NNS, Operator); + } else { + TemplateName Canon = getDependentTemplateName(CanonNNS, Operator); + QTN = new (*this,4) DependentTemplateName(NNS, Operator, Canon); + } + + DependentTemplateNames.InsertNode(QTN, InsertPos); + return TemplateName(QTN); +} + /// getFromTargetType - Given one of the integer types provided by /// TargetInfo, produce the corresponding type. The unsigned @p Type /// is actually a value of type @c TargetInfo::IntType. diff --git a/lib/AST/TemplateName.cpp b/lib/AST/TemplateName.cpp index 24588bc5f1..5b4cf0ad94 100644 --- a/lib/AST/TemplateName.cpp +++ b/lib/AST/TemplateName.cpp @@ -56,7 +56,7 @@ void TemplateName::print(llvm::raw_ostream &OS, const PrintingPolicy &Policy, bool SuppressNNS) const { if (TemplateDecl *Template = Storage.dyn_cast()) - OS << Template->getIdentifier()->getName(); + OS << Template->getNameAsString(); else if (OverloadedFunctionDecl *Ovl = Storage.dyn_cast()) OS << Ovl->getNameAsString(); @@ -70,8 +70,11 @@ TemplateName::print(llvm::raw_ostream &OS, const PrintingPolicy &Policy, if (!SuppressNNS && DTN->getQualifier()) DTN->getQualifier()->print(OS, Policy); OS << "template "; - // FIXME: Shouldn't we have a more general kind of name? - OS << DTN->getName()->getName(); + + if (DTN->isIdentifier()) + OS << DTN->getIdentifier()->getName(); + else + OS << "operator " << getOperatorSpelling(DTN->getOperator()); } } diff --git a/lib/Basic/IdentifierTable.cpp b/lib/Basic/IdentifierTable.cpp index 16aa0c5484..401e6cba06 100644 --- a/lib/Basic/IdentifierTable.cpp +++ b/lib/Basic/IdentifierTable.cpp @@ -381,3 +381,17 @@ SelectorTable::~SelectorTable() { delete &getSelectorTableImpl(Impl); } +const char *clang::getOperatorSpelling(OverloadedOperatorKind Operator) { + switch (Operator) { + case OO_None: + case NUM_OVERLOADED_OPERATORS: + return 0; + +#define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \ + case OO_##Name: return Spelling; +#include "clang/Basic/OperatorKinds.def" + } + + return 0; +} + diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index d0e2f70319..a7ca0c54db 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -109,31 +109,58 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, break; SourceLocation TemplateKWLoc = ConsumeToken(); - - if (Tok.isNot(tok::identifier)) { + + UnqualifiedId TemplateName; + if (Tok.is(tok::identifier)) { + TemplateName.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation()); + + // If the next token is not '<', we may have a stray 'template' keyword. + // Complain and suggest removing the template keyword, but otherwise + // allow parsing to continue. + if (NextToken().isNot(tok::less)) { + Diag(NextToken().getLocation(), + diag::err_less_after_template_name_in_nested_name_spec) + << Tok.getIdentifierInfo()->getName() + << CodeModificationHint::CreateRemoval(SourceRange(TemplateKWLoc)); + break; + } + + // Consume the identifier. + ConsumeToken(); + } else if (Tok.is(tok::kw_operator)) { + if (ParseUnqualifiedIdOperator(SS, EnteringContext, ObjectType, + TemplateName)) + break; + + if (TemplateName.getKind() != UnqualifiedId::IK_OperatorFunctionId) { + Diag(TemplateName.getSourceRange().getBegin(), + diag::err_id_after_template_in_nested_name_spec) + << TemplateName.getSourceRange(); + break; + } else if (Tok.isNot(tok::less)) { + std::string OperatorName = "operator "; + OperatorName += getOperatorSpelling( + TemplateName.OperatorFunctionId.Operator); + Diag(Tok.getLocation(), + diag::err_less_after_template_name_in_nested_name_spec) + << OperatorName + << TemplateName.getSourceRange(); + break; + } + } else { Diag(Tok.getLocation(), diag::err_id_after_template_in_nested_name_spec) << SourceRange(TemplateKWLoc); break; } - if (NextToken().isNot(tok::less)) { - Diag(NextToken().getLocation(), - diag::err_less_after_template_name_in_nested_name_spec) - << Tok.getIdentifierInfo()->getName() - << SourceRange(TemplateKWLoc, Tok.getLocation()); - break; - } - - UnqualifiedId TemplateName; - TemplateName.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation()); TemplateTy Template = Actions.ActOnDependentTemplateName(TemplateKWLoc, SS, TemplateName, ObjectType); if (!Template) break; if (AnnotateTemplateIdToken(Template, TNK_Dependent_template_name, - &SS, TemplateKWLoc, false)) + &SS, TemplateName, TemplateKWLoc, false)) break; continue; @@ -233,8 +260,9 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, // because some clients (e.g., the parsing of class template // specializations) still want to see the original template-id // token. - if (AnnotateTemplateIdToken(Template, TNK, &SS, SourceLocation(), - false)) + ConsumeToken(); + if (AnnotateTemplateIdToken(Template, TNK, &SS, TemplateName, + SourceLocation(), false)) break; continue; } @@ -859,22 +887,17 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, return false; } -/// \brief Parse a C++ unqualified-id (or a C identifier), which describes the -/// name of an entity. +/// \brief Parse an operator-function-id or conversion-function-id as part +/// of a C++ unqualified-id. /// -/// \code -/// unqualified-id: [C++ expr.prim.general] -/// identifier -/// operator-function-id -/// conversion-function-id -/// [C++0x] literal-operator-id [TODO] -/// ~ class-name -/// template-id +/// This routine is responsible only for parsing the operator-function-id or +/// conversion-function-id; it does not handle template arguments in any way. /// +/// \code /// operator-function-id: [C++ 13.5] /// 'operator' operator /// -/// operator: one of +/// operator: one of /// new delete new[] delete[] /// + - * / % ^ & | ~ /// ! = < > += -= *= /= %= @@ -898,6 +921,159 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, /// \param EnteringContext whether we are 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 Result on a successful parse, contains the parsed unqualified-id. +/// +/// \returns true if parsing fails, false otherwise. +bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext, + TypeTy *ObjectType, + UnqualifiedId &Result) { + assert(Tok.is(tok::kw_operator) && "Expected 'operator' keyword"); + + // Consume the 'operator' keyword. + SourceLocation KeywordLoc = ConsumeToken(); + + // Determine what kind of operator name we have. + unsigned SymbolIdx = 0; + SourceLocation SymbolLocations[3]; + OverloadedOperatorKind Op = OO_None; + switch (Tok.getKind()) { + case tok::kw_new: + case tok::kw_delete: { + bool isNew = Tok.getKind() == tok::kw_new; + // Consume the 'new' or 'delete'. + SymbolLocations[SymbolIdx++] = ConsumeToken(); + if (Tok.is(tok::l_square)) { + // Consume the '['. + SourceLocation LBracketLoc = ConsumeBracket(); + // Consume the ']'. + SourceLocation RBracketLoc = MatchRHSPunctuation(tok::r_square, + LBracketLoc); + if (RBracketLoc.isInvalid()) + return true; + + SymbolLocations[SymbolIdx++] = LBracketLoc; + SymbolLocations[SymbolIdx++] = RBracketLoc; + Op = isNew? OO_Array_New : OO_Array_Delete; + } else { + Op = isNew? OO_New : OO_Delete; + } + break; + } + +#define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \ + case tok::Token: \ + SymbolLocations[SymbolIdx++] = ConsumeToken(); \ + Op = OO_##Name; \ + break; +#define OVERLOADED_OPERATOR_MULTI(Name,Spelling,Unary,Binary,MemberOnly) +#include "clang/Basic/OperatorKinds.def" + + case tok::l_paren: { + // Consume the '('. + SourceLocation LParenLoc = ConsumeParen(); + // Consume the ')'. + SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, + LParenLoc); + if (RParenLoc.isInvalid()) + return true; + + SymbolLocations[SymbolIdx++] = LParenLoc; + SymbolLocations[SymbolIdx++] = RParenLoc; + Op = OO_Call; + break; + } + + case tok::l_square: { + // Consume the '['. + SourceLocation LBracketLoc = ConsumeBracket(); + // Consume the ']'. + SourceLocation RBracketLoc = MatchRHSPunctuation(tok::r_square, + LBracketLoc); + if (RBracketLoc.isInvalid()) + return true; + + SymbolLocations[SymbolIdx++] = LBracketLoc; + SymbolLocations[SymbolIdx++] = RBracketLoc; + Op = OO_Subscript; + break; + } + + case tok::code_completion: { + // Code completion for the operator name. + Actions.CodeCompleteOperatorName(CurScope); + + // Consume the operator token. + ConsumeToken(); + + // Don't try to parse any further. + return true; + } + + default: + break; + } + + if (Op != OO_None) { + // We have parsed an operator-function-id. + Result.setOperatorFunctionId(KeywordLoc, Op, SymbolLocations); + return false; + } + + // Parse a conversion-function-id. + // + // conversion-function-id: [C++ 12.3.2] + // operator conversion-type-id + // + // conversion-type-id: + // type-specifier-seq conversion-declarator[opt] + // + // conversion-declarator: + // ptr-operator conversion-declarator[opt] + + // Parse the type-specifier-seq. + DeclSpec DS; + if (ParseCXXTypeSpecifierSeq(DS)) + return true; + + // Parse the conversion-declarator, which is merely a sequence of + // ptr-operators. + Declarator D(DS, Declarator::TypeNameContext); + ParseDeclaratorInternal(D, /*DirectDeclParser=*/0); + + // Finish up the type. + Action::TypeResult Ty = Actions.ActOnTypeName(CurScope, D); + if (Ty.isInvalid()) + return true; + + // Note that this is a conversion-function-id. + Result.setConversionFunctionId(KeywordLoc, Ty.get(), + D.getSourceRange().getEnd()); + return false; +} + +/// \brief Parse a C++ unqualified-id (or a C identifier), which describes the +/// name of an entity. +/// +/// \code +/// unqualified-id: [C++ expr.prim.general] +/// identifier +/// operator-function-id +/// conversion-function-id +/// [C++0x] literal-operator-id [TODO] +/// ~ class-name +/// template-id +/// +/// \endcode +/// +/// \param The nested-name-specifier that preceded this unqualified-id. If +/// non-empty, then we are parsing the unqualified-id of a qualified-id. +/// +/// \param EnteringContext whether we are entering the scope of the +/// nested-name-specifier. +/// /// \param AllowDestructorName whether we allow parsing of a destructor name. /// /// \param AllowConstructorName whether we allow parsing a constructor name. @@ -957,132 +1133,20 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, // operator-function-id // conversion-function-id if (Tok.is(tok::kw_operator)) { - // Consume the 'operator' keyword. - SourceLocation KeywordLoc = ConsumeToken(); - - // Determine what kind of operator name we have. - unsigned SymbolIdx = 0; - SourceLocation SymbolLocations[3]; - OverloadedOperatorKind Op = OO_None; - switch (Tok.getKind()) { - case tok::kw_new: - case tok::kw_delete: { - bool isNew = Tok.getKind() == tok::kw_new; - // Consume the 'new' or 'delete'. - SymbolLocations[SymbolIdx++] = ConsumeToken(); - if (Tok.is(tok::l_square)) { - // Consume the '['. - SourceLocation LBracketLoc = ConsumeBracket(); - // Consume the ']'. - SourceLocation RBracketLoc = MatchRHSPunctuation(tok::r_square, - LBracketLoc); - if (RBracketLoc.isInvalid()) - return true; - - SymbolLocations[SymbolIdx++] = LBracketLoc; - SymbolLocations[SymbolIdx++] = RBracketLoc; - Op = isNew? OO_Array_New : OO_Array_Delete; - } else { - Op = isNew? OO_New : OO_Delete; - } - break; - } - - #define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \ - case tok::Token: \ - SymbolLocations[SymbolIdx++] = ConsumeToken(); \ - Op = OO_##Name; \ - break; - #define OVERLOADED_OPERATOR_MULTI(Name,Spelling,Unary,Binary,MemberOnly) - #include "clang/Basic/OperatorKinds.def" - - case tok::l_paren: { - // Consume the '('. - SourceLocation LParenLoc = ConsumeParen(); - // Consume the ')'. - SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, - LParenLoc); - if (RParenLoc.isInvalid()) - return true; - - SymbolLocations[SymbolIdx++] = LParenLoc; - SymbolLocations[SymbolIdx++] = RParenLoc; - Op = OO_Call; - break; - } - - case tok::l_square: { - // Consume the '['. - SourceLocation LBracketLoc = ConsumeBracket(); - // Consume the ']'. - SourceLocation RBracketLoc = MatchRHSPunctuation(tok::r_square, - LBracketLoc); - if (RBracketLoc.isInvalid()) - return true; - - SymbolLocations[SymbolIdx++] = LBracketLoc; - SymbolLocations[SymbolIdx++] = RBracketLoc; - Op = OO_Subscript; - break; - } - - case tok::code_completion: { - // Code completion for the operator name. - Actions.CodeCompleteOperatorName(CurScope); - - // Consume the operator token. - ConsumeToken(); - - // Don't try to parse any further. - return true; - } - - default: - break; - } - - if (Op != OO_None) { - // We have parsed an operator-function-id. - Result.setOperatorFunctionId(KeywordLoc, Op, SymbolLocations); - - // If the next token is a '<', we may have a template. - if (Tok.is(tok::less)) - return ParseUnqualifiedIdTemplateId(SS, 0, SourceLocation(), - EnteringContext, ObjectType, - Result); - - return false; - } - - // Parse a conversion-function-id. - // - // conversion-function-id: [C++ 12.3.2] - // operator conversion-type-id - // - // conversion-type-id: - // type-specifier-seq conversion-declarator[opt] - // - // conversion-declarator: - // ptr-operator conversion-declarator[opt] - - // Parse the type-specifier-seq. - DeclSpec DS; - if (ParseCXXTypeSpecifierSeq(DS)) + if (ParseUnqualifiedIdOperator(SS, EnteringContext, ObjectType, Result)) return true; - // Parse the conversion-declarator, which is merely a sequence of - // ptr-operators. - Declarator D(DS, Declarator::TypeNameContext); - ParseDeclaratorInternal(D, /*DirectDeclParser=*/0); - - // Finish up the type. - Action::TypeResult Ty = Actions.ActOnTypeName(CurScope, D); - if (Ty.isInvalid()) - return true; + // If we have an operator-function-id and the next token is a '<', we may + // have a + // + // template-id: + // operator-function-id < template-argument-list[opt] > + if (Result.getKind() == UnqualifiedId::IK_OperatorFunctionId && + Tok.is(tok::less)) + return ParseUnqualifiedIdTemplateId(SS, 0, SourceLocation(), + EnteringContext, ObjectType, + Result); - // Note that this is a conversion-function-id. - Result.setConversionFunctionId(KeywordLoc, Ty.get(), - D.getSourceRange().getEnd()); return false; } diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index 64c5a08933..a647720fb9 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -672,22 +672,23 @@ Parser::ParseTemplateIdAfterTemplateName(TemplateTy Template, /// bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK, const CXXScopeSpec *SS, + UnqualifiedId &TemplateName, SourceLocation TemplateKWLoc, bool AllowTypeAnnotation) { assert(getLang().CPlusPlus && "Can only annotate template-ids in C++"); - assert(Template && Tok.is(tok::identifier) && NextToken().is(tok::less) && + assert(Template && Tok.is(tok::less) && "Parser isn't at the beginning of a template-id"); // Consume the template-name. - IdentifierInfo *Name = Tok.getIdentifierInfo(); - SourceLocation TemplateNameLoc = ConsumeToken(); + SourceLocation TemplateNameLoc = TemplateName.getSourceRange().getBegin(); // Parse the enclosed template argument list. SourceLocation LAngleLoc, RAngleLoc; TemplateArgList TemplateArgs; TemplateArgIsTypeList TemplateArgIsType; TemplateArgLocationList TemplateArgLocations; - bool Invalid = ParseTemplateIdAfterTemplateName(Template, TemplateNameLoc, + bool Invalid = ParseTemplateIdAfterTemplateName(Template, + TemplateNameLoc, SS, false, LAngleLoc, TemplateArgs, TemplateArgIsType, @@ -736,7 +737,13 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK, TemplateIdAnnotation *TemplateId = TemplateIdAnnotation::Allocate(TemplateArgs.size()); TemplateId->TemplateNameLoc = TemplateNameLoc; - TemplateId->Name = Name; + if (TemplateName.getKind() == UnqualifiedId::IK_Identifier) { + TemplateId->Name = TemplateName.Identifier; + TemplateId->Operator = OO_None; + } else { + TemplateId->Name = 0; + TemplateId->Operator = TemplateName.OperatorFunctionId.Operator; + } TemplateId->Template = Template.getAs(); TemplateId->Kind = TNK; TemplateId->LAngleLoc = LAngleLoc; diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index d1baa91b65..6836c307d3 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -940,13 +940,16 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) { if (TemplateNameKind TNK = Actions.isTemplateName(CurScope, SS, TemplateName, /*ObjectType=*/0, EnteringContext, - Template)) - if (AnnotateTemplateIdToken(Template, TNK, &SS)) { + Template)) { + // Consume the identifier. + ConsumeToken(); + if (AnnotateTemplateIdToken(Template, TNK, &SS, TemplateName)) { // If an unrecoverable error occurred, we need to return true here, // because the token stream is in a damaged state. We may not return // a valid identifier. return Tok.isNot(tok::identifier); } + } } // The current token, which is either an identifier or a diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index bdf1a7e010..4dd3c2dbef 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -2498,8 +2498,13 @@ Sema::OwningExprResult Sema::ActOnMemberAccessExpr(Scope *S, ExprArg Base, Name = ActualTemplate->getDeclName(); else if (OverloadedFunctionDecl *Ovl = Template.getAsOverloadedFunctionDecl()) Name = Ovl->getDeclName(); - else - Name = Template.getAsDependentTemplateName()->getName(); + else { + DependentTemplateName *DTN = Template.getAsDependentTemplateName(); + if (DTN->isIdentifier()) + Name = DTN->getIdentifier(); + else + Name = Context.DeclarationNames.getCXXOperatorName(DTN->getOperator()); + } // Translate the parser's template argument list in our AST format. ASTTemplateArgsPtr TemplateArgsPtr(*this, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 2e1a89e658..3c56358d5a 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -1405,6 +1405,10 @@ Sema::ActOnDependentTemplateName(SourceLocation TemplateKWLoc, return TemplateTy::make(Context.getDependentTemplateName(Qualifier, Name.Identifier)); + case UnqualifiedId::IK_OperatorFunctionId: + return TemplateTy::make(Context.getDependentTemplateName(Qualifier, + Name.OperatorFunctionId.Operator)); + default: break; } diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 2a4fa8bcef..8a5699e7ec 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -605,7 +605,17 @@ public: const IdentifierInfo &II, QualType ObjectType); - + /// \brief Build a new template name given a nested name specifier and the + /// overloaded operator name that is referred to as a template. + /// + /// By default, performs semantic analysis to determine whether the name can + /// be resolved to a specific template, then builds the appropriate kind of + /// template name. Subclasses may override this routine to provide different + /// behavior. + TemplateName RebuildTemplateName(NestedNameSpecifier *Qualifier, + OverloadedOperatorKind Operator, + QualType ObjectType); + /// \brief Build a new compound statement. /// /// By default, performs semantic analysis to build the new statement. @@ -1575,9 +1585,14 @@ public: else if (OverloadedFunctionDecl *Ovl = Template.getAsOverloadedFunctionDecl()) Name = Ovl->getDeclName(); - else - Name = Template.getAsDependentTemplateName()->getName(); - + else { + DependentTemplateName *DTN = Template.getAsDependentTemplateName(); + if (DTN->isIdentifier()) + Name = DTN->getIdentifier(); + else + Name = SemaRef.Context.DeclarationNames.getCXXOperatorName( + DTN->getOperator()); + } return SemaRef.BuildMemberReferenceExpr(/*Scope=*/0, move(Base), OperatorLoc, OpKind, TemplateNameLoc, Name, true, @@ -1873,7 +1888,12 @@ TreeTransform::TransformTemplateName(TemplateName Name, ObjectType.isNull()) return Name; - return getDerived().RebuildTemplateName(NNS, *DTN->getName(), ObjectType); + if (DTN->isIdentifier()) + return getDerived().RebuildTemplateName(NNS, *DTN->getIdentifier(), + ObjectType); + + return getDerived().RebuildTemplateName(NNS, DTN->getOperator(), + ObjectType); } if (TemplateDecl *Template = Name.getAsTemplateDecl()) { @@ -4749,9 +4769,14 @@ TreeTransform::TransformCXXUnresolvedMemberExpr( // FIXME: This is an ugly hack, which forces the same template name to // be looked up multiple times. Yuck! - // FIXME: This also won't work for, e.g., x->template operator+ - TemplateName OrigTemplateName - = SemaRef.Context.getDependentTemplateName(0, Name.getAsIdentifierInfo()); + TemporaryBase Rebase(*this, E->getMemberLoc(), DeclarationName()); + TemplateName OrigTemplateName; + if (const IdentifierInfo *II = Name.getAsIdentifierInfo()) + OrigTemplateName = SemaRef.Context.getDependentTemplateName(0, II); + else + OrigTemplateName + = SemaRef.Context.getDependentTemplateName(0, + Name.getCXXOverloadedOperator()); TemplateName Template = getDerived().TransformTemplateName(OrigTemplateName, @@ -5194,6 +5219,26 @@ TreeTransform::RebuildTemplateName(NestedNameSpecifier *Qualifier, .template getAsVal(); } +template +TemplateName +TreeTransform::RebuildTemplateName(NestedNameSpecifier *Qualifier, + OverloadedOperatorKind Operator, + QualType ObjectType) { + CXXScopeSpec SS; + SS.setRange(SourceRange(getDerived().getBaseLocation())); + SS.setScopeRep(Qualifier); + UnqualifiedId Name; + SourceLocation SymbolLocations[3]; // FIXME: Bogus location information. + Name.setOperatorFunctionId(/*FIXME:*/getDerived().getBaseLocation(), + Operator, SymbolLocations); + return getSema().ActOnDependentTemplateName( + /*FIXME:*/getDerived().getBaseLocation(), + SS, + Name, + ObjectType.getAsOpaquePtr()) + .template getAsVal(); +} + template Sema::OwningExprResult TreeTransform::RebuildCXXOperatorCallExpr(OverloadedOperatorKind Op, diff --git a/test/SemaCXX/invalid-template-specifier.cpp b/test/SemaCXX/invalid-template-specifier.cpp index a3f081ff60..034ad73b08 100644 --- a/test/SemaCXX/invalid-template-specifier.cpp +++ b/test/SemaCXX/invalid-template-specifier.cpp @@ -8,5 +8,5 @@ const template basic_istream; // expected-error {{expected unqualified-id} namespace S {} template class Y { void x() { S::template y(1); } // expected-error {{does not refer to a template}} \ - // expected-error {{no member named 'y'}} + // expected-error {{unqualified-id}} }; diff --git a/test/SemaTemplate/operator-function-id-template.cpp b/test/SemaTemplate/operator-function-id-template.cpp index d21c46d120..92a8c84e3b 100644 --- a/test/SemaTemplate/operator-function-id-template.cpp +++ b/test/SemaTemplate/operator-function-id-template.cpp @@ -19,9 +19,10 @@ void test_op(A a, int i) { template void test_op_template(A at, T x) { - // FIXME: Not yet implemented. - // const A &atr = at.template operator+(x); - // const A &atr2 = at.A::template operator+(x); + const A &atr = at.template operator+(x); + const A &atr2 = at.A::template operator+(x); + // FIXME: unrelated template-name instantiation issue + // const A &atr3 = at.template A::template operator+(x); } template void test_op_template(A, float);