[modules] Suport for merging a parsed enum definition into an existing imported but not visible definition.

llvm-svn: 236690
This commit is contained in:
Richard Smith 2015-05-07 03:54:19 +00:00
parent 69a4779965
commit d9ba224f66
7 changed files with 93 additions and 25 deletions

View File

@ -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);

View File

@ -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

View File

@ -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)

View File

@ -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<EnumConstantDecl>(PrevDecl);
NamedDecl *Hidden;
if (PrevECD &&
!hasVisibleDefinition(cast<NamedDecl>(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,

View File

@ -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<FunctionDecl>(Entity)) {

View File

@ -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<CXXRecordDecl>(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;
}

View File

@ -29,3 +29,10 @@ template<typename T> struct F {
};
template<typename T> int F<T>::f() { return 0; }
template<typename T> template<typename U> int F<T>::g() { return 0; }
namespace G {
enum A { a, b, c, d, e };
enum { f, g, h };
typedef enum { i, j } k;
typedef enum {} l;
}