From: Richard Smith Date: Thu, 7 May 2015 03:54:19 +0000 (+0000) Subject: [modules] Suport for merging a parsed enum definition into an existing imported but... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=93517ddb4ab4da8d3fa26d04a0d2d58e03fccec0;p=clang [modules] Suport for merging a parsed enum definition into an existing imported but not visible definition. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@236690 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 8a265f4c59..76c87b1dda 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1279,6 +1279,10 @@ private: bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T, TypeDiagnoser &Diagnoser); public: + /// \brief Make a merged definition of an existing hidden definition \p ND + /// visible at the specified location. + void makeMergedDefinitionVisible(NamedDecl *ND, SourceLocation Loc); + /// Determine if \p D has a visible definition. If not, suggest a declaration /// that should be made visible to expose the definition. bool hasVisibleDefinition(NamedDecl *D, NamedDecl **Suggested); @@ -1724,6 +1728,12 @@ public: TUK_Friend // Friend declaration: 'friend struct foo;' }; + struct SkipBodyInfo { + SkipBodyInfo() : ShouldSkip(false), Previous(nullptr) {} + bool ShouldSkip; + NamedDecl *Previous; + }; + Decl *ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc, @@ -1733,7 +1743,7 @@ public: bool &OwnedDecl, bool &IsDependent, SourceLocation ScopedEnumKWLoc, bool ScopedEnumUsesClassTag, TypeResult UnderlyingType, - bool IsTypeSpecifier, bool *SkipBody = nullptr); + bool IsTypeSpecifier, SkipBodyInfo *SkipBody = nullptr); Decl *ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc, unsigned TagSpec, SourceLocation TagLoc, @@ -1844,6 +1854,11 @@ public: bool CheckEnumRedeclaration(SourceLocation EnumLoc, bool IsScoped, QualType EnumUnderlyingTy, const EnumDecl *Prev); + /// Determine whether the body of an anonymous enumeration should be skipped. + /// \param II The name of the first enumerator. + SkipBodyInfo shouldSkipAnonEnumBody(Scope *S, IdentifierInfo *II, + SourceLocation IILoc); + Decl *ActOnEnumConstant(Scope *S, Decl *EnumDecl, Decl *LastEnumConstant, SourceLocation IdLoc, IdentifierInfo *Id, AttributeList *Attrs, @@ -5382,7 +5397,7 @@ public: SourceLocation FriendLoc, unsigned NumOuterTemplateParamLists, TemplateParameterList **OuterTemplateParamLists, - bool *SkipBody = nullptr); + SkipBodyInfo *SkipBody = nullptr); void translateTemplateArguments(const ASTTemplateArgsPtr &In, TemplateArgumentListInfo &Out); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 16639f04ce..5ad673aef1 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3893,6 +3893,13 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, handleDeclspecAlignBeforeClassKey(attrs, DS, TUK); + Sema::SkipBodyInfo SkipBody; + if (!Name && TUK == Sema::TUK_Definition && Tok.is(tok::l_brace) && + NextToken().is(tok::identifier)) + SkipBody = Actions.shouldSkipAnonEnumBody(getCurScope(), + NextToken().getIdentifierInfo(), + NextToken().getLocation()); + bool Owned = false; bool IsDependent = false; const char *PrevSpec = nullptr; @@ -3902,7 +3909,22 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, AS, DS.getModulePrivateSpecLoc(), TParams, Owned, IsDependent, ScopedEnumKWLoc, IsScopedUsingClassTag, BaseType, - DSC == DSC_type_specifier); + DSC == DSC_type_specifier, &SkipBody); + + if (SkipBody.ShouldSkip) { + assert(TUK == Sema::TUK_Definition && "can only skip a definition"); + + BalancedDelimiterTracker T(*this, tok::l_brace); + T.consumeOpen(); + T.skipToEnd(); + + if (DS.SetTypeSpecType(DeclSpec::TST_enum, StartLoc, + NameLoc.isValid() ? NameLoc : StartLoc, + PrevSpec, DiagID, TagDecl, Owned, + Actions.getASTContext().getPrintingPolicy())) + Diag(StartLoc, DiagID) << PrevSpec; + return; + } if (IsDependent) { // This enum has a dependent nested-name-specifier. Handle it as a diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 11934c4df6..bb7ae63764 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -1553,7 +1553,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, TypeResult TypeResult = true; // invalid bool Owned = false; - bool SkipBody = false; + Sema::SkipBodyInfo SkipBody; if (TemplateId) { // Explicit specialization, class template partial specialization, // or explicit instantiation. @@ -1718,7 +1718,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, assert(Tok.is(tok::l_brace) || (getLangOpts().CPlusPlus && Tok.is(tok::colon)) || isCXX11FinalKeyword()); - if (SkipBody) + if (SkipBody.ShouldSkip) SkipCXXMemberSpecification(StartLoc, AttrFixitLoc, TagType, TagOrTempResult.get()); else if (getLangOpts().CPlusPlus) diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 7f4b395280..bc31d4612e 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -16,7 +16,6 @@ #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTLambda.h" -#include "clang/AST/ASTMutationListener.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/CharUnits.h" #include "clang/AST/CommentDiagnostic.h" @@ -1974,9 +1973,7 @@ void Sema::MergeTypedefNameDecl(TypedefNameDecl *New, LookupResult &OldDecls) { New->setTypeSourceInfo(OldTD->getTypeSourceInfo()); // Make the old tag definition visible. - if (auto *Listener = getASTMutationListener()) - Listener->RedefinedHiddenDefinition(Hidden, NewTag->getLocation()); - Hidden->setHidden(false); + makeMergedDefinitionVisible(Hidden, NewTag->getLocation()); } } @@ -11311,8 +11308,8 @@ static FixItHint createFriendTagNNSFixIt(Sema &SemaRef, NamedDecl *ND, Scope *S, /// \param IsTypeSpecifier \c true if this is a type-specifier (or /// trailing-type-specifier) other than one in an alias-declaration. /// -/// \param SkipBody If non-null, will be set to true if the caller should skip -/// the definition of this tag, and treat it as if it were a declaration. +/// \param SkipBody If non-null, will be set to indicate if the caller should +/// skip the definition of this tag and treat it as if it were a declaration. Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc, @@ -11323,7 +11320,7 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation ScopedEnumKWLoc, bool ScopedEnumUsesClassTag, TypeResult UnderlyingType, - bool IsTypeSpecifier, bool *SkipBody) { + bool IsTypeSpecifier, SkipBodyInfo *SkipBody) { // If this is not a definition, it must have a name. IdentifierInfo *OrigName = Name; assert((Name != nullptr || TUK == TUK_Definition) && @@ -11633,6 +11630,10 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, } } + // If we have a known previous declaration to use, then use it. + if (Previous.empty() && SkipBody && SkipBody->Previous) + Previous.addDecl(SkipBody->Previous); + if (!Previous.empty()) { NamedDecl *PrevDecl = Previous.getFoundDecl(); NamedDecl *DirectPrevDecl = @@ -11774,10 +11775,8 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // assume that this definition is identical to the hidden one // we already have. Make the existing definition visible and // use it in place of this one. - *SkipBody = true; - if (auto *Listener = getASTMutationListener()) - Listener->RedefinedHiddenDefinition(Hidden, KWLoc); - Hidden->setHidden(false); + SkipBody->ShouldSkip = true; + makeMergedDefinitionVisible(Hidden, KWLoc); return Def; } else if (!IsExplicitSpecializationAfterInstantiation) { // A redeclaration in function prototype scope in C isn't @@ -13465,6 +13464,29 @@ EnumConstantDecl *Sema::CheckEnumConstant(EnumDecl *Enum, Val, EnumVal); } +Sema::SkipBodyInfo Sema::shouldSkipAnonEnumBody(Scope *S, IdentifierInfo *II, + SourceLocation IILoc) { + if (!getLangOpts().Modules || !getLangOpts().CPlusPlus) + return SkipBodyInfo(); + + // We have an anonymous enum definition. Look up the first enumerator to + // determine if we should merge the definition with an existing one and + // skip the body. + NamedDecl *PrevDecl = LookupSingleName(S, II, IILoc, LookupOrdinaryName, + ForRedeclaration); + auto *PrevECD = dyn_cast_or_null(PrevDecl); + NamedDecl *Hidden; + if (PrevECD && + !hasVisibleDefinition(cast(PrevECD->getDeclContext()), + &Hidden)) { + SkipBodyInfo Skip; + Skip.ShouldSkip = true; + Skip.Previous = Hidden; + return Skip; + } + + return SkipBodyInfo(); +} Decl *Sema::ActOnEnumConstant(Scope *S, Decl *theEnumDecl, Decl *lastEnumConst, SourceLocation IdLoc, IdentifierInfo *Id, diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index 09424a48d6..012c1cf392 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/Sema/Lookup.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/ASTMutationListener.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" @@ -1169,6 +1170,12 @@ static Decl *getInstantiatedFrom(Decl *D, MemberSpecializationInfo *MSInfo) { return MSInfo->isExplicitSpecialization() ? D : MSInfo->getInstantiatedFrom(); } +void Sema::makeMergedDefinitionVisible(NamedDecl *ND, SourceLocation Loc) { + if (auto *Listener = getASTMutationListener()) + Listener->RedefinedHiddenDefinition(ND, Loc); + ND->setHidden(false); +} + /// \brief Find the module in which the given declaration was defined. static Module *getDefiningModule(Decl *Entity) { if (FunctionDecl *FD = dyn_cast(Entity)) { diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index c642c0599b..687e6111f2 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -12,7 +12,6 @@ #include "TreeTransform.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" -#include "clang/AST/ASTMutationListener.h" #include "clang/AST/DeclFriend.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" @@ -838,7 +837,7 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation FriendLoc, unsigned NumOuterTemplateParamLists, TemplateParameterList** OuterTemplateParamLists, - bool *SkipBody) { + SkipBodyInfo *SkipBody) { assert(TemplateParams && TemplateParams->size() > 0 && "No template parameters"); assert(TUK != TUK_Reference && "Can only declare or define class templates"); @@ -999,16 +998,12 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, // simply making that previous definition visible. NamedDecl *Hidden = nullptr; if (SkipBody && !hasVisibleDefinition(Def, &Hidden)) { - *SkipBody = true; + SkipBody->ShouldSkip = true; auto *Tmpl = cast(Hidden)->getDescribedClassTemplate(); assert(Tmpl && "original definition of a class template is not a " "class template?"); - if (auto *Listener = getASTMutationListener()) { - Listener->RedefinedHiddenDefinition(Hidden, KWLoc); - Listener->RedefinedHiddenDefinition(Tmpl, KWLoc); - } - Hidden->setHidden(false); - Tmpl->setHidden(false); + makeMergedDefinitionVisible(Hidden, KWLoc); + makeMergedDefinitionVisible(Tmpl, KWLoc); return Def; } diff --git a/test/Modules/Inputs/submodules-merge-defs/defs.h b/test/Modules/Inputs/submodules-merge-defs/defs.h index 89e4b2319c..ccf1398797 100644 --- a/test/Modules/Inputs/submodules-merge-defs/defs.h +++ b/test/Modules/Inputs/submodules-merge-defs/defs.h @@ -29,3 +29,10 @@ template struct F { }; template int F::f() { return 0; } template template int F::g() { return 0; } + +namespace G { + enum A { a, b, c, d, e }; + enum { f, g, h }; + typedef enum { i, j } k; + typedef enum {} l; +}