forked from OSchip/llvm-project
[modules] If we reach a definition of a class for which we already have a
non-visible definition, skip the new definition and make the old one visible instead of trying to parse it again and failing horribly. C++'s ODR allows us to assume that the two definitions are identical. llvm-svn: 233250
This commit is contained in:
parent
e972c36221
commit
65ebb4ac8a
|
@ -24,6 +24,7 @@ namespace clang {
|
||||||
class DeclContext;
|
class DeclContext;
|
||||||
class FunctionDecl;
|
class FunctionDecl;
|
||||||
class FunctionTemplateDecl;
|
class FunctionTemplateDecl;
|
||||||
|
class NamedDecl;
|
||||||
class ObjCCategoryDecl;
|
class ObjCCategoryDecl;
|
||||||
class ObjCContainerDecl;
|
class ObjCContainerDecl;
|
||||||
class ObjCInterfaceDecl;
|
class ObjCInterfaceDecl;
|
||||||
|
@ -113,6 +114,12 @@ public:
|
||||||
/// \param D the declaration marked OpenMP threadprivate.
|
/// \param D the declaration marked OpenMP threadprivate.
|
||||||
virtual void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) {}
|
virtual void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) {}
|
||||||
|
|
||||||
|
/// \brief A definition has been made visible by being redefined locally.
|
||||||
|
///
|
||||||
|
/// \param D The definition that was previously not visible.
|
||||||
|
virtual void RedefinedHiddenDefinition(const NamedDecl *D,
|
||||||
|
SourceLocation Loc) {}
|
||||||
|
|
||||||
// NOTE: If new methods are added they should also be added to
|
// NOTE: If new methods are added they should also be added to
|
||||||
// MultiplexASTMutationListener.
|
// MultiplexASTMutationListener.
|
||||||
};
|
};
|
||||||
|
|
|
@ -2284,6 +2284,10 @@ private:
|
||||||
AccessSpecifier AS, bool EnteringContext,
|
AccessSpecifier AS, bool EnteringContext,
|
||||||
DeclSpecContext DSC,
|
DeclSpecContext DSC,
|
||||||
ParsedAttributesWithRange &Attributes);
|
ParsedAttributesWithRange &Attributes);
|
||||||
|
void SkipCXXMemberSpecification(SourceLocation StartLoc,
|
||||||
|
SourceLocation AttrFixitLoc,
|
||||||
|
unsigned TagType,
|
||||||
|
Decl *TagDecl);
|
||||||
void ParseCXXMemberSpecification(SourceLocation StartLoc,
|
void ParseCXXMemberSpecification(SourceLocation StartLoc,
|
||||||
SourceLocation AttrFixitLoc,
|
SourceLocation AttrFixitLoc,
|
||||||
ParsedAttributesWithRange &Attrs,
|
ParsedAttributesWithRange &Attrs,
|
||||||
|
|
|
@ -1279,6 +1279,10 @@ private:
|
||||||
bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T,
|
bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T,
|
||||||
TypeDiagnoser &Diagnoser);
|
TypeDiagnoser &Diagnoser);
|
||||||
public:
|
public:
|
||||||
|
/// 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);
|
||||||
|
|
||||||
bool RequireCompleteType(SourceLocation Loc, QualType T,
|
bool RequireCompleteType(SourceLocation Loc, QualType T,
|
||||||
TypeDiagnoser &Diagnoser);
|
TypeDiagnoser &Diagnoser);
|
||||||
bool RequireCompleteType(SourceLocation Loc, QualType T,
|
bool RequireCompleteType(SourceLocation Loc, QualType T,
|
||||||
|
@ -1725,7 +1729,7 @@ public:
|
||||||
bool &OwnedDecl, bool &IsDependent,
|
bool &OwnedDecl, bool &IsDependent,
|
||||||
SourceLocation ScopedEnumKWLoc,
|
SourceLocation ScopedEnumKWLoc,
|
||||||
bool ScopedEnumUsesClassTag, TypeResult UnderlyingType,
|
bool ScopedEnumUsesClassTag, TypeResult UnderlyingType,
|
||||||
bool IsTypeSpecifier);
|
bool IsTypeSpecifier, bool *SkipBody = nullptr);
|
||||||
|
|
||||||
Decl *ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc,
|
Decl *ActOnTemplatedFriendTag(Scope *S, SourceLocation FriendLoc,
|
||||||
unsigned TagSpec, SourceLocation TagLoc,
|
unsigned TagSpec, SourceLocation TagLoc,
|
||||||
|
@ -1790,6 +1794,11 @@ public:
|
||||||
/// struct, or union).
|
/// struct, or union).
|
||||||
void ActOnTagStartDefinition(Scope *S, Decl *TagDecl);
|
void ActOnTagStartDefinition(Scope *S, Decl *TagDecl);
|
||||||
|
|
||||||
|
/// \brief Invoked when we enter a tag definition that we're skipping.
|
||||||
|
void ActOnTagStartSkippedDefinition(Scope *S, Decl *TD) {
|
||||||
|
PushDeclContext(S, cast<DeclContext>(TD));
|
||||||
|
}
|
||||||
|
|
||||||
Decl *ActOnObjCContainerStartDefinition(Decl *IDecl);
|
Decl *ActOnObjCContainerStartDefinition(Decl *IDecl);
|
||||||
|
|
||||||
/// ActOnStartCXXMemberDeclarations - Invoked when we have parsed a
|
/// ActOnStartCXXMemberDeclarations - Invoked when we have parsed a
|
||||||
|
@ -1805,6 +1814,10 @@ public:
|
||||||
void ActOnTagFinishDefinition(Scope *S, Decl *TagDecl,
|
void ActOnTagFinishDefinition(Scope *S, Decl *TagDecl,
|
||||||
SourceLocation RBraceLoc);
|
SourceLocation RBraceLoc);
|
||||||
|
|
||||||
|
void ActOnTagFinishSkippedDefinition() {
|
||||||
|
PopDeclContext();
|
||||||
|
}
|
||||||
|
|
||||||
void ActOnObjCContainerFinishDefinition();
|
void ActOnObjCContainerFinishDefinition();
|
||||||
|
|
||||||
/// \brief Invoked when we must temporarily exit the objective-c container
|
/// \brief Invoked when we must temporarily exit the objective-c container
|
||||||
|
|
|
@ -861,6 +861,8 @@ public:
|
||||||
const ObjCCategoryDecl *ClassExt) override;
|
const ObjCCategoryDecl *ClassExt) override;
|
||||||
void DeclarationMarkedUsed(const Decl *D) override;
|
void DeclarationMarkedUsed(const Decl *D) override;
|
||||||
void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) override;
|
void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) override;
|
||||||
|
void RedefinedHiddenDefinition(const NamedDecl *D,
|
||||||
|
SourceLocation Loc) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief AST and semantic-analysis consumer that generates a
|
/// \brief AST and semantic-analysis consumer that generates a
|
||||||
|
|
|
@ -110,6 +110,8 @@ public:
|
||||||
const ObjCCategoryDecl *ClassExt) override;
|
const ObjCCategoryDecl *ClassExt) override;
|
||||||
void DeclarationMarkedUsed(const Decl *D) override;
|
void DeclarationMarkedUsed(const Decl *D) override;
|
||||||
void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) override;
|
void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) override;
|
||||||
|
void RedefinedHiddenDefinition(const NamedDecl *D,
|
||||||
|
SourceLocation Loc) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<ASTMutationListener*> Listeners;
|
std::vector<ASTMutationListener*> Listeners;
|
||||||
|
@ -193,6 +195,11 @@ void MultiplexASTMutationListener::DeclarationMarkedOpenMPThreadPrivate(
|
||||||
for (size_t i = 0, e = Listeners.size(); i != e; ++i)
|
for (size_t i = 0, e = Listeners.size(); i != e; ++i)
|
||||||
Listeners[i]->DeclarationMarkedOpenMPThreadPrivate(D);
|
Listeners[i]->DeclarationMarkedOpenMPThreadPrivate(D);
|
||||||
}
|
}
|
||||||
|
void MultiplexASTMutationListener::RedefinedHiddenDefinition(
|
||||||
|
const NamedDecl *D, SourceLocation Loc) {
|
||||||
|
for (auto *L : Listeners)
|
||||||
|
L->RedefinedHiddenDefinition(D, Loc);
|
||||||
|
}
|
||||||
|
|
||||||
} // end namespace clang
|
} // end namespace clang
|
||||||
|
|
||||||
|
|
|
@ -1550,6 +1550,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
|
||||||
TypeResult TypeResult = true; // invalid
|
TypeResult TypeResult = true; // invalid
|
||||||
|
|
||||||
bool Owned = false;
|
bool Owned = false;
|
||||||
|
bool SkipBody = false;
|
||||||
if (TemplateId) {
|
if (TemplateId) {
|
||||||
// Explicit specialization, class template partial specialization,
|
// Explicit specialization, class template partial specialization,
|
||||||
// or explicit instantiation.
|
// or explicit instantiation.
|
||||||
|
@ -1695,7 +1696,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
|
||||||
TParams, Owned, IsDependent,
|
TParams, Owned, IsDependent,
|
||||||
SourceLocation(), false,
|
SourceLocation(), false,
|
||||||
clang::TypeResult(),
|
clang::TypeResult(),
|
||||||
DSC == DSC_type_specifier);
|
DSC == DSC_type_specifier,
|
||||||
|
&SkipBody);
|
||||||
|
|
||||||
// If ActOnTag said the type was dependent, try again with the
|
// If ActOnTag said the type was dependent, try again with the
|
||||||
// less common call.
|
// less common call.
|
||||||
|
@ -1711,7 +1713,10 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
|
||||||
assert(Tok.is(tok::l_brace) ||
|
assert(Tok.is(tok::l_brace) ||
|
||||||
(getLangOpts().CPlusPlus && Tok.is(tok::colon)) ||
|
(getLangOpts().CPlusPlus && Tok.is(tok::colon)) ||
|
||||||
isCXX11FinalKeyword());
|
isCXX11FinalKeyword());
|
||||||
if (getLangOpts().CPlusPlus)
|
if (SkipBody)
|
||||||
|
SkipCXXMemberSpecification(StartLoc, AttrFixitLoc, TagType,
|
||||||
|
TagOrTempResult.get());
|
||||||
|
else if (getLangOpts().CPlusPlus)
|
||||||
ParseCXXMemberSpecification(StartLoc, AttrFixitLoc, attrs, TagType,
|
ParseCXXMemberSpecification(StartLoc, AttrFixitLoc, attrs, TagType,
|
||||||
TagOrTempResult.get());
|
TagOrTempResult.get());
|
||||||
else
|
else
|
||||||
|
@ -2688,6 +2693,55 @@ ExprResult Parser::ParseCXXMemberInitializer(Decl *D, bool IsFunction,
|
||||||
return ParseInitializer();
|
return ParseInitializer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Parser::SkipCXXMemberSpecification(SourceLocation RecordLoc,
|
||||||
|
SourceLocation AttrFixitLoc,
|
||||||
|
unsigned TagType, Decl *TagDecl) {
|
||||||
|
// Skip the optional 'final' keyword.
|
||||||
|
if (getLangOpts().CPlusPlus && Tok.is(tok::identifier)) {
|
||||||
|
assert(isCXX11FinalKeyword() && "not a class definition");
|
||||||
|
ConsumeToken();
|
||||||
|
|
||||||
|
// Diagnose any C++11 attributes after 'final' keyword.
|
||||||
|
// We deliberately discard these attributes.
|
||||||
|
ParsedAttributesWithRange Attrs(AttrFactory);
|
||||||
|
CheckMisplacedCXX11Attribute(Attrs, AttrFixitLoc);
|
||||||
|
|
||||||
|
// This can only happen if we had malformed misplaced attributes;
|
||||||
|
// we only get called if there is a colon or left-brace after the
|
||||||
|
// attributes.
|
||||||
|
if (Tok.isNot(tok::colon) && Tok.isNot(tok::l_brace))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the base clauses. This requires actually parsing them, because
|
||||||
|
// otherwise we can't be sure where they end (a left brace may appear
|
||||||
|
// within a template argument).
|
||||||
|
if (Tok.is(tok::colon)) {
|
||||||
|
// Enter the scope of the class so that we can correctly parse its bases.
|
||||||
|
ParseScope ClassScope(this, Scope::ClassScope|Scope::DeclScope);
|
||||||
|
ParsingClassDefinition ParsingDef(*this, TagDecl, /*NonNestedClass*/ true,
|
||||||
|
TagType == DeclSpec::TST_interface);
|
||||||
|
Actions.ActOnTagStartSkippedDefinition(getCurScope(), TagDecl);
|
||||||
|
|
||||||
|
// Parse the bases but don't attach them to the class.
|
||||||
|
ParseBaseClause(nullptr);
|
||||||
|
|
||||||
|
Actions.ActOnTagFinishSkippedDefinition();
|
||||||
|
|
||||||
|
if (!Tok.is(tok::l_brace)) {
|
||||||
|
Diag(PP.getLocForEndOfToken(PrevTokLocation),
|
||||||
|
diag::err_expected_lbrace_after_base_specifiers);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the body.
|
||||||
|
assert(Tok.is(tok::l_brace));
|
||||||
|
BalancedDelimiterTracker T(*this, tok::l_brace);
|
||||||
|
T.consumeOpen();
|
||||||
|
T.skipToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
/// ParseCXXMemberSpecification - Parse the class definition.
|
/// ParseCXXMemberSpecification - Parse the class definition.
|
||||||
///
|
///
|
||||||
/// member-specification:
|
/// member-specification:
|
||||||
|
|
|
@ -218,6 +218,7 @@ bool Sema::RequireCompleteDeclContext(CXXScopeSpec &SS,
|
||||||
// Fixed enum types are complete, but they aren't valid as scopes
|
// Fixed enum types are complete, but they aren't valid as scopes
|
||||||
// until we see a definition, so awkwardly pull out this special
|
// until we see a definition, so awkwardly pull out this special
|
||||||
// case.
|
// case.
|
||||||
|
// FIXME: The definition might not be visible; complain if it is not.
|
||||||
const EnumType *enumType = dyn_cast_or_null<EnumType>(tagType);
|
const EnumType *enumType = dyn_cast_or_null<EnumType>(tagType);
|
||||||
if (!enumType || enumType->getDecl()->isCompleteDefinition())
|
if (!enumType || enumType->getDecl()->isCompleteDefinition())
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "clang/AST/ASTConsumer.h"
|
#include "clang/AST/ASTConsumer.h"
|
||||||
#include "clang/AST/ASTContext.h"
|
#include "clang/AST/ASTContext.h"
|
||||||
#include "clang/AST/ASTLambda.h"
|
#include "clang/AST/ASTLambda.h"
|
||||||
|
#include "clang/AST/ASTMutationListener.h"
|
||||||
#include "clang/AST/CXXInheritance.h"
|
#include "clang/AST/CXXInheritance.h"
|
||||||
#include "clang/AST/CharUnits.h"
|
#include "clang/AST/CharUnits.h"
|
||||||
#include "clang/AST/CommentDiagnostic.h"
|
#include "clang/AST/CommentDiagnostic.h"
|
||||||
|
@ -11233,13 +11234,16 @@ static FixItHint createFriendTagNNSFixIt(Sema &SemaRef, NamedDecl *ND, Scope *S,
|
||||||
return FixItHint::CreateInsertion(NameLoc, Insertion);
|
return FixItHint::CreateInsertion(NameLoc, Insertion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ActOnTag - This is invoked when we see 'struct foo' or 'struct {'. In the
|
/// \brief This is invoked when we see 'struct foo' or 'struct {'. In the
|
||||||
/// former case, Name will be non-null. In the later case, Name will be null.
|
/// former case, Name will be non-null. In the later case, Name will be null.
|
||||||
/// TagSpec indicates what kind of tag this is. TUK indicates whether this is a
|
/// TagSpec indicates what kind of tag this is. TUK indicates whether this is a
|
||||||
/// reference/declaration/definition of a tag.
|
/// reference/declaration/definition of a tag.
|
||||||
///
|
///
|
||||||
/// IsTypeSpecifier is true if this is a type-specifier (or
|
/// \param IsTypeSpecifier \c true if this is a type-specifier (or
|
||||||
/// trailing-type-specifier) other than one in an alias-declaration.
|
/// 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.
|
||||||
Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
|
Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
|
||||||
SourceLocation KWLoc, CXXScopeSpec &SS,
|
SourceLocation KWLoc, CXXScopeSpec &SS,
|
||||||
IdentifierInfo *Name, SourceLocation NameLoc,
|
IdentifierInfo *Name, SourceLocation NameLoc,
|
||||||
|
@ -11250,7 +11254,7 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
|
||||||
SourceLocation ScopedEnumKWLoc,
|
SourceLocation ScopedEnumKWLoc,
|
||||||
bool ScopedEnumUsesClassTag,
|
bool ScopedEnumUsesClassTag,
|
||||||
TypeResult UnderlyingType,
|
TypeResult UnderlyingType,
|
||||||
bool IsTypeSpecifier) {
|
bool IsTypeSpecifier, bool *SkipBody) {
|
||||||
// If this is not a definition, it must have a name.
|
// If this is not a definition, it must have a name.
|
||||||
IdentifierInfo *OrigName = Name;
|
IdentifierInfo *OrigName = Name;
|
||||||
assert((Name != nullptr || TUK == TUK_Definition) &&
|
assert((Name != nullptr || TUK == TUK_Definition) &&
|
||||||
|
@ -11676,7 +11680,7 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
|
||||||
|
|
||||||
// Diagnose attempts to redefine a tag.
|
// Diagnose attempts to redefine a tag.
|
||||||
if (TUK == TUK_Definition) {
|
if (TUK == TUK_Definition) {
|
||||||
if (TagDecl *Def = PrevTagDecl->getDefinition()) {
|
if (NamedDecl *Def = PrevTagDecl->getDefinition()) {
|
||||||
// If we're defining a specialization and the previous definition
|
// If we're defining a specialization and the previous definition
|
||||||
// is from an implicit instantiation, don't emit an error
|
// is from an implicit instantiation, don't emit an error
|
||||||
// here; we'll catch this in the general case below.
|
// here; we'll catch this in the general case below.
|
||||||
|
@ -11692,7 +11696,19 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
|
||||||
TSK_ExplicitSpecialization;
|
TSK_ExplicitSpecialization;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsExplicitSpecializationAfterInstantiation) {
|
if (SkipBody && getLangOpts().CPlusPlus &&
|
||||||
|
!hasVisibleDefinition(Def, &Def)) {
|
||||||
|
// There is a definition of this tag, but it is not visible. We
|
||||||
|
// explicitly make use of C++'s one definition rule here, and
|
||||||
|
// 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(Def, KWLoc);
|
||||||
|
Def->setHidden(false);
|
||||||
|
return Def;
|
||||||
|
} else if (!IsExplicitSpecializationAfterInstantiation) {
|
||||||
// A redeclaration in function prototype scope in C isn't
|
// A redeclaration in function prototype scope in C isn't
|
||||||
// visible elsewhere, so merely issue a warning.
|
// visible elsewhere, so merely issue a warning.
|
||||||
if (!getLangOpts().CPlusPlus && S->containedInPrototypeScope())
|
if (!getLangOpts().CPlusPlus && S->containedInPrototypeScope())
|
||||||
|
|
|
@ -5126,9 +5126,9 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
|
||||||
/// \param D The definition of the entity.
|
/// \param D The definition of the entity.
|
||||||
/// \param Suggested Filled in with the declaration that should be made visible
|
/// \param Suggested Filled in with the declaration that should be made visible
|
||||||
/// in order to provide a definition of this entity.
|
/// in order to provide a definition of this entity.
|
||||||
static bool hasVisibleDefinition(Sema &S, NamedDecl *D, NamedDecl **Suggested) {
|
bool Sema::hasVisibleDefinition(NamedDecl *D, NamedDecl **Suggested) {
|
||||||
// Easy case: if we don't have modules, all declarations are visible.
|
// Easy case: if we don't have modules, all declarations are visible.
|
||||||
if (!S.getLangOpts().Modules)
|
if (!getLangOpts().Modules)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// If this definition was instantiated from a template, map back to the
|
// If this definition was instantiated from a template, map back to the
|
||||||
|
@ -5144,7 +5144,7 @@ static bool hasVisibleDefinition(Sema &S, NamedDecl *D, NamedDecl **Suggested) {
|
||||||
// If the enum has a fixed underlying type, any declaration of it will do.
|
// If the enum has a fixed underlying type, any declaration of it will do.
|
||||||
*Suggested = nullptr;
|
*Suggested = nullptr;
|
||||||
for (auto *Redecl : ED->redecls()) {
|
for (auto *Redecl : ED->redecls()) {
|
||||||
if (LookupResult::isVisible(S, Redecl))
|
if (LookupResult::isVisible(*this, Redecl))
|
||||||
return true;
|
return true;
|
||||||
if (Redecl->isThisDeclarationADefinition() ||
|
if (Redecl->isThisDeclarationADefinition() ||
|
||||||
(Redecl->isCanonicalDecl() && !*Suggested))
|
(Redecl->isCanonicalDecl() && !*Suggested))
|
||||||
|
@ -5159,7 +5159,7 @@ static bool hasVisibleDefinition(Sema &S, NamedDecl *D, NamedDecl **Suggested) {
|
||||||
// FIXME: If we merged any other decl into D, and that declaration is visible,
|
// FIXME: If we merged any other decl into D, and that declaration is visible,
|
||||||
// then we should consider a definition to be visible.
|
// then we should consider a definition to be visible.
|
||||||
*Suggested = D;
|
*Suggested = D;
|
||||||
return LookupResult::isVisible(S, D);
|
return LookupResult::isVisible(*this, D);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Locks in the inheritance model for the given class and all of its bases.
|
/// Locks in the inheritance model for the given class and all of its bases.
|
||||||
|
@ -5210,7 +5210,7 @@ bool Sema::RequireCompleteTypeImpl(SourceLocation Loc, QualType T,
|
||||||
// If we know about the definition but it is not visible, complain.
|
// If we know about the definition but it is not visible, complain.
|
||||||
NamedDecl *SuggestedDef = nullptr;
|
NamedDecl *SuggestedDef = nullptr;
|
||||||
if (!Diagnoser.Suppressed && Def &&
|
if (!Diagnoser.Suppressed && Def &&
|
||||||
!hasVisibleDefinition(*this, Def, &SuggestedDef)) {
|
!hasVisibleDefinition(Def, &SuggestedDef)) {
|
||||||
// Suppress this error outside of a SFINAE context if we've already
|
// Suppress this error outside of a SFINAE context if we've already
|
||||||
// emitted the error once for this type. There's no usefulness in
|
// emitted the error once for this type. There's no usefulness in
|
||||||
// repeating the diagnostic.
|
// repeating the diagnostic.
|
||||||
|
|
|
@ -35,7 +35,8 @@ enum DeclUpdateKind {
|
||||||
UPD_DECL_MARKED_USED,
|
UPD_DECL_MARKED_USED,
|
||||||
UPD_MANGLING_NUMBER,
|
UPD_MANGLING_NUMBER,
|
||||||
UPD_STATIC_LOCAL_NUMBER,
|
UPD_STATIC_LOCAL_NUMBER,
|
||||||
UPD_DECL_MARKED_OPENMP_THREADPRIVATE
|
UPD_DECL_MARKED_OPENMP_THREADPRIVATE,
|
||||||
|
UPD_DECL_EXPORTED
|
||||||
};
|
};
|
||||||
|
|
||||||
TypeIdx TypeIdxFromBuiltin(const BuiltinType *BT);
|
TypeIdx TypeIdxFromBuiltin(const BuiltinType *BT);
|
||||||
|
|
|
@ -3793,10 +3793,24 @@ void ASTDeclReader::UpdateDecl(Decl *D, ModuleFile &ModuleFile,
|
||||||
case UPD_STATIC_LOCAL_NUMBER:
|
case UPD_STATIC_LOCAL_NUMBER:
|
||||||
Reader.Context.setStaticLocalNumber(cast<VarDecl>(D), Record[Idx++]);
|
Reader.Context.setStaticLocalNumber(cast<VarDecl>(D), Record[Idx++]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UPD_DECL_MARKED_OPENMP_THREADPRIVATE:
|
case UPD_DECL_MARKED_OPENMP_THREADPRIVATE:
|
||||||
D->addAttr(OMPThreadPrivateDeclAttr::CreateImplicit(
|
D->addAttr(OMPThreadPrivateDeclAttr::CreateImplicit(
|
||||||
Reader.Context, ReadSourceRange(Record, Idx)));
|
Reader.Context, ReadSourceRange(Record, Idx)));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case UPD_DECL_EXPORTED:
|
||||||
|
unsigned SubmoduleID = readSubmoduleID(Record, Idx);
|
||||||
|
Module *Owner = SubmoduleID ? Reader.getSubmodule(SubmoduleID) : nullptr;
|
||||||
|
if (Owner && Owner->NameVisibility != Module::AllVisible) {
|
||||||
|
// If Owner is made visible at some later point, make this declaration
|
||||||
|
// visible too.
|
||||||
|
Reader.HiddenNamesMap[Owner].HiddenDecls.push_back(D);
|
||||||
|
} else {
|
||||||
|
// The declaration is now visible.
|
||||||
|
D->Hidden = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4927,10 +4927,15 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {
|
||||||
case UPD_STATIC_LOCAL_NUMBER:
|
case UPD_STATIC_LOCAL_NUMBER:
|
||||||
Record.push_back(Update.getNumber());
|
Record.push_back(Update.getNumber());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UPD_DECL_MARKED_OPENMP_THREADPRIVATE:
|
case UPD_DECL_MARKED_OPENMP_THREADPRIVATE:
|
||||||
AddSourceRange(D->getAttr<OMPThreadPrivateDeclAttr>()->getRange(),
|
AddSourceRange(D->getAttr<OMPThreadPrivateDeclAttr>()->getRange(),
|
||||||
Record);
|
Record);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case UPD_DECL_EXPORTED:
|
||||||
|
Record.push_back(inferSubmoduleIDFromLocation(Update.getLoc()));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6074,3 +6079,11 @@ void ASTWriter::DeclarationMarkedOpenMPThreadPrivate(const Decl *D) {
|
||||||
|
|
||||||
DeclUpdates[D].push_back(DeclUpdate(UPD_DECL_MARKED_OPENMP_THREADPRIVATE));
|
DeclUpdates[D].push_back(DeclUpdate(UPD_DECL_MARKED_OPENMP_THREADPRIVATE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ASTWriter::RedefinedHiddenDefinition(const NamedDecl *D,
|
||||||
|
SourceLocation Loc) {
|
||||||
|
assert(!WritingAST && "Already writing the AST!");
|
||||||
|
assert(D->isHidden() && "expected a hidden declaration");
|
||||||
|
assert(D->isFromASTFile() && "hidden decl not from AST file");
|
||||||
|
DeclUpdates[D].push_back(DeclUpdate(UPD_DECL_EXPORTED, Loc));
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
struct A {};
|
||||||
|
class B {
|
||||||
|
struct Inner1 {};
|
||||||
|
struct Inner2;
|
||||||
|
};
|
||||||
|
struct B::Inner2 : Inner1 {};
|
|
@ -0,0 +1,5 @@
|
||||||
|
// Trigger import of definitions, but don't make them visible.
|
||||||
|
#include "empty.h"
|
||||||
|
|
||||||
|
// Now parse the definitions again.
|
||||||
|
#include "defs.h"
|
|
@ -0,0 +1,11 @@
|
||||||
|
module "stuff" {
|
||||||
|
textual header "defs.h"
|
||||||
|
module "empty" { header "empty.h" }
|
||||||
|
module "use" { header "use-defs.h" }
|
||||||
|
}
|
||||||
|
|
||||||
|
module "redef" {
|
||||||
|
header "import-and-redefine.h"
|
||||||
|
// Do not re-export stuff.use
|
||||||
|
use "stuff"
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
#include "defs.h"
|
|
@ -0,0 +1,13 @@
|
||||||
|
// RUN: rm -rf %t
|
||||||
|
// RUN: %clang_cc1 -x c++ -fmodules-cache-path=%t -fmodules -I %S/Inputs/submodules-merge-defs %s -verify -fno-modules-error-recovery
|
||||||
|
|
||||||
|
// Trigger import of definitions, but don't make them visible.
|
||||||
|
#include "empty.h"
|
||||||
|
|
||||||
|
A pre_a; // expected-error {{must be imported}} expected-error {{must use 'struct'}}
|
||||||
|
// expected-note@defs.h:1 {{here}}
|
||||||
|
|
||||||
|
// Make definitions from second module visible.
|
||||||
|
#include "import-and-redefine.h"
|
||||||
|
|
||||||
|
A post_a;
|
Loading…
Reference in New Issue