From 014e88d94ff83e3aad4e33b16413a2d1817ec208 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 3 Nov 2009 23:16:33 +0000 Subject: [PATCH] Parsing and semantic analysis for template-ids that name overloaded operators, e.g., operator+ which now works in declarators, id-expressions, and member access expressions. This commit only implements the non-dependent case, where we can resolve the template-id to an actual declaration. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@85966 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticParseKinds.td | 2 - include/clang/Lex/Token.h | 4 + include/clang/Parse/Action.h | 46 +++++------ include/clang/Parse/DeclSpec.h | 4 +- lib/Parse/MinimalAction.cpp | 5 +- lib/Parse/ParseExprCXX.cpp | 62 +++++++-------- lib/Parse/Parser.cpp | 5 +- lib/Sema/Sema.h | 8 +- lib/Sema/SemaTemplate.cpp | 77 +++++++++++++------ lib/Sema/TreeTransform.h | 5 +- .../operator-function-id-template.cpp | 27 +++++++ 11 files changed, 148 insertions(+), 97 deletions(-) create mode 100644 test/SemaTemplate/operator-function-id-template.cpp diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index e27a5df143..db8d580a2c 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -295,8 +295,6 @@ def err_variadic_templates : Error< // C++ declarations def err_friend_decl_defines_class : Error< "cannot define a type in a friend declaration">; -def warn_operator_template_id_ignores_args : Error< - "clang bug: ignoring template arguments provided for operator">; // Language specific pragmas // - Generic warnings diff --git a/include/clang/Lex/Token.h b/include/clang/Lex/Token.h index 8acdb30cc7..fa7ba54478 100644 --- a/include/clang/Lex/Token.h +++ b/include/clang/Lex/Token.h @@ -17,6 +17,7 @@ #include "clang/Basic/TemplateKinds.h" #include "clang/Basic/TokenKinds.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Basic/OperatorKinds.h" #include namespace clang { @@ -261,6 +262,9 @@ struct TemplateIdAnnotation { /// FIXME: Temporarily stores the name of a specialization IdentifierInfo *Name; + /// FIXME: Temporarily stores the overloaded operator kind. + OverloadedOperatorKind Operator; + /// The declaration of the template corresponding to the /// template-name. This is an Action::DeclTy*. void *Template; diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index d362259995..2989eb6adb 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -236,30 +236,36 @@ public: virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S, const CXXScopeSpec *SS = 0) = 0; - /// \brief Determine whether the given identifier refers to the name of a + /// \brief Determine whether the given name refers to a template. + /// + /// This callback is used by the parser after it has seen a '<' to determine + /// whether the given name refers to a template and, if so, what kind of /// template. /// - /// \param S the scope in which name lookup occurs + /// \param S the scope in which the name occurs. /// - /// \param II the identifier that we are querying to determine whether it - /// is a template. + /// \param SS the C++ nested-name-specifier that precedes the template name, + /// if any. /// - /// \param IdLoc the source location of the identifier + /// \param Name the name that we are querying to determine whether it is + /// a template. /// - /// \param SS the C++ scope specifier that precedes the template name, if - /// any. + /// \param ObjectType if we are determining whether the given name is a + /// template name in the context of a member access expression (e.g., + /// \c p->X), this is the type of the object referred to by the + /// member access (e.g., \c p). /// /// \param EnteringContext whether we are potentially entering the context - /// referred to by the scope specifier \p SS + /// referred to by the nested-name-specifier \p SS, which allows semantic + /// analysis to look into uninstantiated templates. /// /// \param Template if the name does refer to a template, the declaration /// of the template that the name refers to. /// /// \returns the kind of template that this name refers to. virtual TemplateNameKind isTemplateName(Scope *S, - const IdentifierInfo &II, - SourceLocation IdLoc, - const CXXScopeSpec *SS, + const CXXScopeSpec &SS, + UnqualifiedId &Name, TypeTy *ObjectType, bool EnteringContext, TemplateTy &Template) = 0; @@ -1616,22 +1622,19 @@ public: /// /// \param TemplateKWLoc the location of the "template" keyword (if any). /// - /// \param Name the name of the template (an identifier) - /// - /// \param NameLoc the location of the identifier - /// /// \param SS the nested-name-specifier that precedes the "template" keyword - /// or the template name. FIXME: If the dependent template name occurs in + /// or the template name. If the dependent template name occurs in /// a member access expression, e.g., "x.template f", this /// nested-name-specifier will be empty. /// + /// \param Name the name of the template. + /// /// \param ObjectType if this dependent template name occurs in the /// context of a member access expression, the type of the object being /// accessed. virtual TemplateTy ActOnDependentTemplateName(SourceLocation TemplateKWLoc, - const IdentifierInfo &Name, - SourceLocation NameLoc, const CXXScopeSpec &SS, + UnqualifiedId &Name, TypeTy *ObjectType) { return TemplateTy(); } @@ -2333,13 +2336,12 @@ public: const CXXScopeSpec *SS); virtual TemplateNameKind isTemplateName(Scope *S, - const IdentifierInfo &II, - SourceLocation IdLoc, - const CXXScopeSpec *SS, + const CXXScopeSpec &SS, + UnqualifiedId &Name, TypeTy *ObjectType, bool EnteringContext, TemplateTy &Template); - + /// ActOnDeclarator - If this is a typedef declarator, we modify the /// IdentifierInfo::FETokenInfo field to keep track of this fact, until S is /// popped. diff --git a/include/clang/Parse/DeclSpec.h b/include/clang/Parse/DeclSpec.h index 4fb4c8cc0c..dccb8bcfeb 100644 --- a/include/clang/Parse/DeclSpec.h +++ b/include/clang/Parse/DeclSpec.h @@ -561,9 +561,9 @@ public: /// /// \param Id the parsed identifier. /// \param IdLoc the location of the parsed identifier. - void setIdentifier(IdentifierInfo *Id, SourceLocation IdLoc) { + void setIdentifier(const IdentifierInfo *Id, SourceLocation IdLoc) { Kind = IK_Identifier; - Identifier = Id; + Identifier = const_cast(Id); StartLocation = EndLocation = IdLoc; } diff --git a/lib/Parse/MinimalAction.cpp b/lib/Parse/MinimalAction.cpp index 71b22cad6f..1e7d397fdf 100644 --- a/lib/Parse/MinimalAction.cpp +++ b/lib/Parse/MinimalAction.cpp @@ -161,9 +161,8 @@ bool MinimalAction::isCurrentClassName(const IdentifierInfo &, Scope *, TemplateNameKind MinimalAction::isTemplateName(Scope *S, - const IdentifierInfo &II, - SourceLocation IdLoc, - const CXXScopeSpec *SS, + const CXXScopeSpec &SS, + UnqualifiedId &Name, TypeTy *ObjectType, bool EnteringScope, TemplateTy &TemplateDecl) { diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index d5314c0d4e..d0e2f70319 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -125,10 +125,10 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, break; } + UnqualifiedId TemplateName; + TemplateName.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation()); TemplateTy Template - = Actions.ActOnDependentTemplateName(TemplateKWLoc, - *Tok.getIdentifierInfo(), - Tok.getLocation(), SS, + = Actions.ActOnDependentTemplateName(TemplateKWLoc, SS, TemplateName, ObjectType); if (!Template) break; @@ -220,9 +220,10 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, // type-name '<' if (Next.is(tok::less)) { TemplateTy Template; - if (TemplateNameKind TNK = Actions.isTemplateName(CurScope, II, - Tok.getLocation(), - &SS, + UnqualifiedId TemplateName; + TemplateName.setIdentifier(&II, Tok.getLocation()); + if (TemplateNameKind TNK = Actions.isTemplateName(CurScope, SS, + TemplateName, ObjectType, EnteringContext, Template)) { @@ -741,45 +742,30 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, TemplateNameKind TNK = TNK_Non_template; switch (Id.getKind()) { case UnqualifiedId::IK_Identifier: - TNK = Actions.isTemplateName(CurScope, *Id.Identifier, Id.StartLocation, - &SS, ObjectType, EnteringContext, Template); + case UnqualifiedId::IK_OperatorFunctionId: + TNK = Actions.isTemplateName(CurScope, SS, Id, ObjectType, EnteringContext, + Template); break; - case UnqualifiedId::IK_OperatorFunctionId: { - // FIXME: Temporary hack: warn that we are completely ignoring the - // template arguments for now. - // Parse the enclosed template argument list and throw it away. - SourceLocation LAngleLoc, RAngleLoc; - TemplateArgList TemplateArgs; - TemplateArgIsTypeList TemplateArgIsType; - TemplateArgLocationList TemplateArgLocations; - if (ParseTemplateIdAfterTemplateName(Template, Id.StartLocation, - &SS, true, LAngleLoc, - TemplateArgs, - TemplateArgIsType, - TemplateArgLocations, - RAngleLoc)) - return true; - - Diag(Id.StartLocation, diag::warn_operator_template_id_ignores_args) - << SourceRange(LAngleLoc, RAngleLoc); + case UnqualifiedId::IK_ConstructorName: { + UnqualifiedId TemplateName; + TemplateName.setIdentifier(Name, NameLoc); + TNK = Actions.isTemplateName(CurScope, SS, TemplateName, ObjectType, + EnteringContext, Template); break; } - case UnqualifiedId::IK_ConstructorName: - TNK = Actions.isTemplateName(CurScope, *Name, NameLoc, &SS, ObjectType, - EnteringContext, Template); - break; - - case UnqualifiedId::IK_DestructorName: + case UnqualifiedId::IK_DestructorName: { + UnqualifiedId TemplateName; + TemplateName.setIdentifier(Name, NameLoc); if (ObjectType) { - Template = Actions.ActOnDependentTemplateName(SourceLocation(), *Name, - NameLoc, SS, ObjectType); + Template = Actions.ActOnDependentTemplateName(SourceLocation(), SS, + TemplateName, ObjectType); TNK = TNK_Dependent_template_name; if (!Template.get()) return true; } else { - TNK = Actions.isTemplateName(CurScope, *Name, NameLoc, &SS, ObjectType, + TNK = Actions.isTemplateName(CurScope, SS, TemplateName, ObjectType, EnteringContext, Template); if (TNK == TNK_Non_template && Id.DestructorName == 0) { @@ -794,6 +780,7 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, } } break; + } default: return false; @@ -824,9 +811,12 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, if (Id.getKind() == UnqualifiedId::IK_Identifier) { TemplateId->Name = Id.Identifier; + TemplateId->Operator = OO_None; TemplateId->TemplateNameLoc = Id.StartLocation; } else { - // FIXME: Handle IK_OperatorFunctionId + TemplateId->Name = 0; + TemplateId->Operator = Id.OperatorFunctionId.Operator; + TemplateId->TemplateNameLoc = Id.StartLocation; } TemplateId->Template = Template.getAs(); diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index cc4aec6297..d1baa91b65 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -935,9 +935,10 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) { // If this is a template-id, annotate with a template-id or type token. if (NextToken().is(tok::less)) { TemplateTy Template; + UnqualifiedId TemplateName; + TemplateName.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation()); if (TemplateNameKind TNK - = Actions.isTemplateName(CurScope, *Tok.getIdentifierInfo(), - Tok.getLocation(), &SS, + = Actions.isTemplateName(CurScope, SS, TemplateName, /*ObjectType=*/0, EnteringContext, Template)) if (AnnotateTemplateIdToken(Template, TNK, &SS)) { diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index bcbbeb2f80..53f0d7a192 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2397,9 +2397,8 @@ public: // C++ Templates [C++ 14] // virtual TemplateNameKind isTemplateName(Scope *S, - const IdentifierInfo &II, - SourceLocation IdLoc, - const CXXScopeSpec *SS, + const CXXScopeSpec &SS, + UnqualifiedId &Name, TypeTy *ObjectType, bool EnteringContext, TemplateTy &Template); @@ -2499,9 +2498,8 @@ public: SourceLocation RAngleLoc); virtual TemplateTy ActOnDependentTemplateName(SourceLocation TemplateKWLoc, - const IdentifierInfo &Name, - SourceLocation NameLoc, const CXXScopeSpec &SS, + UnqualifiedId &Name, TypeTy *ObjectType); bool CheckClassTemplatePartialSpecializationArgs( diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index efaab20c4e..2e1a89e658 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -98,29 +98,43 @@ static NamedDecl *isAcceptableTemplateName(ASTContext &Context, NamedDecl *D) { } TemplateNameKind Sema::isTemplateName(Scope *S, - const IdentifierInfo &II, - SourceLocation IdLoc, - const CXXScopeSpec *SS, + const CXXScopeSpec &SS, + UnqualifiedId &Name, TypeTy *ObjectTypePtr, bool EnteringContext, TemplateTy &TemplateResult) { + DeclarationName TName; + + switch (Name.getKind()) { + case UnqualifiedId::IK_Identifier: + TName = DeclarationName(Name.Identifier); + break; + + case UnqualifiedId::IK_OperatorFunctionId: + TName = Context.DeclarationNames.getCXXOperatorName( + Name.OperatorFunctionId.Operator); + break; + + default: + return TNK_Non_template; + } + // Determine where to perform name lookup DeclContext *LookupCtx = 0; bool isDependent = false; if (ObjectTypePtr) { // This nested-name-specifier occurs in a member access expression, e.g., // x->B::f, and we are looking into the type of the object. - assert((!SS || !SS->isSet()) && - "ObjectType and scope specifier cannot coexist"); + assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist"); QualType ObjectType = QualType::getFromOpaquePtr(ObjectTypePtr); LookupCtx = computeDeclContext(ObjectType); isDependent = ObjectType->isDependentType(); - } else if (SS && SS->isSet()) { + } else if (SS.isSet()) { // This nested-name-specifier occurs after another nested-name-specifier, // so long into the context associated with the prior nested-name-specifier. - LookupCtx = computeDeclContext(*SS, EnteringContext); - isDependent = isDependentScopeSpecifier(*SS); + LookupCtx = computeDeclContext(SS, EnteringContext); + isDependent = isDependentScopeSpecifier(SS); } LookupResult Found; @@ -132,10 +146,10 @@ TemplateNameKind Sema::isTemplateName(Scope *S, // nested-name-specifier. // The declaration context must be complete. - if (!LookupCtx->isDependentContext() && RequireCompleteDeclContext(*SS)) + if (!LookupCtx->isDependentContext() && RequireCompleteDeclContext(SS)) return TNK_Non_template; - LookupQualifiedName(Found, LookupCtx, &II, LookupOrdinaryName); + LookupQualifiedName(Found, LookupCtx, TName, LookupOrdinaryName); if (ObjectTypePtr && Found.getKind() == LookupResult::NotFound) { // C++ [basic.lookup.classref]p1: @@ -150,7 +164,7 @@ TemplateNameKind Sema::isTemplateName(Scope *S, // // FIXME: When we're instantiating a template, do we actually have to // look in the scope of the template? Seems fishy... - LookupName(Found, S, &II, LookupOrdinaryName); + LookupName(Found, S, TName, LookupOrdinaryName); ObjectTypeSearchedInScope = true; } } else if (isDependent) { @@ -158,7 +172,7 @@ TemplateNameKind Sema::isTemplateName(Scope *S, return TNK_Non_template; } else { // Perform unqualified name lookup in the current scope. - LookupName(Found, S, &II, LookupOrdinaryName); + LookupName(Found, S, TName, LookupOrdinaryName); } // FIXME: Cope with ambiguous name-lookup results. @@ -177,7 +191,7 @@ TemplateNameKind Sema::isTemplateName(Scope *S, // postfix-expression and [...] // LookupResult FoundOuter; - LookupName(FoundOuter, S, &II, LookupOrdinaryName); + LookupName(FoundOuter, S, TName, LookupOrdinaryName); // FIXME: Handle ambiguities in this lookup better NamedDecl *OuterTemplate = isAcceptableTemplateName(Context, FoundOuter.getAsSingleDecl(Context)); @@ -194,8 +208,10 @@ TemplateNameKind Sema::isTemplateName(Scope *S, // entity as the one found in the class of the object expression, // otherwise the program is ill-formed. if (OuterTemplate->getCanonicalDecl() != Template->getCanonicalDecl()) { - Diag(IdLoc, diag::err_nested_name_member_ref_lookup_ambiguous) - << &II; + Diag(Name.getSourceRange().getBegin(), + diag::err_nested_name_member_ref_lookup_ambiguous) + << TName + << Name.getSourceRange(); Diag(Template->getLocation(), diag::note_ambig_member_ref_object_type) << QualType::getFromOpaquePtr(ObjectTypePtr); Diag(OuterTemplate->getLocation(), diag::note_ambig_member_ref_scope); @@ -206,9 +222,9 @@ TemplateNameKind Sema::isTemplateName(Scope *S, } } - if (SS && SS->isSet() && !SS->isInvalid()) { + if (SS.isSet() && !SS.isInvalid()) { NestedNameSpecifier *Qualifier - = static_cast(SS->getScopeRep()); + = static_cast(SS.getScopeRep()); if (OverloadedFunctionDecl *Ovl = dyn_cast(Template)) TemplateResult @@ -1345,9 +1361,8 @@ Sema::OwningExprResult Sema::ActOnTemplateIdExpr(const CXXScopeSpec &SS, /// of the "template" keyword, and "apply" is the \p Name. Sema::TemplateTy Sema::ActOnDependentTemplateName(SourceLocation TemplateKWLoc, - const IdentifierInfo &Name, - SourceLocation NameLoc, const CXXScopeSpec &SS, + UnqualifiedId &Name, TypeTy *ObjectType) { if ((ObjectType && computeDeclContext(QualType::getFromOpaquePtr(ObjectType))) || @@ -1369,11 +1384,13 @@ Sema::ActOnDependentTemplateName(SourceLocation TemplateKWLoc, // "template" keyword is now permitted). We follow the C++0x // rules, even in C++03 mode, retroactively applying the DR. TemplateTy Template; - TemplateNameKind TNK = isTemplateName(0, Name, NameLoc, &SS, ObjectType, + TemplateNameKind TNK = isTemplateName(0, SS, Name, ObjectType, false, Template); if (TNK == TNK_Non_template) { - Diag(NameLoc, diag::err_template_kw_refers_to_non_template) - << &Name; + Diag(Name.getSourceRange().getBegin(), + diag::err_template_kw_refers_to_non_template) + << GetNameFromUnqualifiedId(Name) + << Name.getSourceRange(); return TemplateTy(); } @@ -1382,7 +1399,21 @@ Sema::ActOnDependentTemplateName(SourceLocation TemplateKWLoc, NestedNameSpecifier *Qualifier = static_cast(SS.getScopeRep()); - return TemplateTy::make(Context.getDependentTemplateName(Qualifier, &Name)); + + switch (Name.getKind()) { + case UnqualifiedId::IK_Identifier: + return TemplateTy::make(Context.getDependentTemplateName(Qualifier, + Name.Identifier)); + + default: + break; + } + + Diag(Name.getSourceRange().getBegin(), + diag::err_template_kw_refers_to_non_template) + << GetNameFromUnqualifiedId(Name) + << Name.getSourceRange(); + return TemplateTy(); } bool Sema::CheckTemplateTypeArgument(TemplateTypeParmDecl *Param, diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index d3f46694f3..2a4fa8bcef 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -5184,11 +5184,12 @@ TreeTransform::RebuildTemplateName(NestedNameSpecifier *Qualifier, CXXScopeSpec SS; SS.setRange(SourceRange(getDerived().getBaseLocation())); SS.setScopeRep(Qualifier); + UnqualifiedId Name; + Name.setIdentifier(&II, /*FIXME:*/getDerived().getBaseLocation()); return getSema().ActOnDependentTemplateName( - /*FIXME:*/getDerived().getBaseLocation(), - II, /*FIXME:*/getDerived().getBaseLocation(), SS, + Name, ObjectType.getAsOpaquePtr()) .template getAsVal(); } diff --git a/test/SemaTemplate/operator-function-id-template.cpp b/test/SemaTemplate/operator-function-id-template.cpp new file mode 100644 index 0000000000..d21c46d120 --- /dev/null +++ b/test/SemaTemplate/operator-function-id-template.cpp @@ -0,0 +1,27 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +template +struct A { + template A operator+(U); +}; + +template bool operator==(A, A); + +template<> bool operator==<0>(A, A); + +bool test_qualified_id(A ai) { + return ::operator==<0, int>(ai, ai); +} + +void test_op(A a, int i) { + const A &air = a.operator+(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); +} + +template void test_op_template(A, float);