From: Richard Smith Date: Fri, 27 Mar 2015 00:41:57 +0000 (+0000) Subject: [modules] Handle defining a class template on top of an existing imported-but-not... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4babb8dca1481c027c38ceaed8bf6c7c52ae509a;p=clang [modules] Handle defining a class template on top of an existing imported-but-not-visible definition. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@233341 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Lookup.h b/include/clang/Sema/Lookup.h index 8ba78bea59..5bfee8b0d0 100644 --- a/include/clang/Sema/Lookup.h +++ b/include/clang/Sema/Lookup.h @@ -291,9 +291,6 @@ public: if (!D->isHidden()) return true; - if (SemaRef.ActiveTemplateInstantiations.empty()) - return false; - // During template instantiation, we can refer to hidden declarations, if // they were visible in any module along the path of instantiation. return isVisibleSlow(SemaRef, D); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 21fe2e493c..6cf86e29da 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -5344,7 +5344,8 @@ public: SourceLocation ModulePrivateLoc, SourceLocation FriendLoc, unsigned NumOuterTemplateParamLists, - TemplateParameterList **OuterTemplateParamLists); + TemplateParameterList **OuterTemplateParamLists, + bool *SkipBody = nullptr); void translateTemplateArguments(const ASTTemplateArgsPtr &In, TemplateArgumentListInfo &Out); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index ffd22390ee..e9c40aa2e5 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -11297,7 +11297,8 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, ModulePrivateLoc, /*FriendLoc*/SourceLocation(), TemplateParameterLists.size()-1, - TemplateParameterLists.data()); + TemplateParameterLists.data(), + SkipBody); return Result.get(); } else { // The "template<>" header is extraneous. @@ -11696,8 +11697,9 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, TSK_ExplicitSpecialization; } + NamedDecl *Hidden = nullptr; if (SkipBody && getLangOpts().CPlusPlus && - !hasVisibleDefinition(Def, &Def)) { + !hasVisibleDefinition(Def, &Hidden)) { // 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 @@ -11705,8 +11707,8 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // use it in place of this one. *SkipBody = true; if (auto *Listener = getASTMutationListener()) - Listener->RedefinedHiddenDefinition(Def, KWLoc); - Def->setHidden(false); + Listener->RedefinedHiddenDefinition(Hidden, KWLoc); + Hidden->setHidden(false); return Def; } else if (!IsExplicitSpecializationAfterInstantiation) { // A redeclaration in function prototype scope in C isn't diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index 0b40618405..ac7376efbc 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -1217,11 +1217,27 @@ llvm::DenseSet &Sema::getLookupModules() { /// path (by instantiating a template, you allow it to see the declarations that /// your module can see, including those later on in your module). bool LookupResult::isVisibleSlow(Sema &SemaRef, NamedDecl *D) { - assert(D->isHidden() && !SemaRef.ActiveTemplateInstantiations.empty() && - "should not call this: not in slow case"); + assert(D->isHidden() && "should not call this: not in slow case"); Module *DeclModule = D->getOwningModule(); assert(DeclModule && "hidden decl not from a module"); + // If this declaration is not at namespace scope nor module-private, + // then it is visible if its lexical parent has a visible definition. + DeclContext *DC = D->getLexicalDeclContext(); + if (!D->isModulePrivate() && + DC && !DC->isFileContext() && !isa(DC)) { + NamedDecl *Hidden; + if (SemaRef.hasVisibleDefinition(cast(DC), &Hidden)) { + if (SemaRef.ActiveTemplateInstantiations.empty()) { + // Cache the fact that this declaration is implicitly visible because + // its parent has a visible definition. + D->setHidden(false); + } + return true; + } + return false; + } + // Find the extra places where we need to look. llvm::DenseSet &LookupModules = SemaRef.getLookupModules(); if (LookupModules.empty()) diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 805709dab8..c642c0599b 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -12,6 +12,7 @@ #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" @@ -836,7 +837,8 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, AccessSpecifier AS, SourceLocation ModulePrivateLoc, SourceLocation FriendLoc, unsigned NumOuterTemplateParamLists, - TemplateParameterList** OuterTemplateParamLists) { + TemplateParameterList** OuterTemplateParamLists, + bool *SkipBody) { assert(TemplateParams && TemplateParams->size() > 0 && "No template parameters"); assert(TUK != TUK_Reference && "Can only declare or define class templates"); @@ -993,6 +995,23 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, // Check for redefinition of this class template. if (TUK == TUK_Definition) { if (TagDecl *Def = PrevRecordDecl->getDefinition()) { + // If we have a prior definition that is not visible, treat this as + // simply making that previous definition visible. + NamedDecl *Hidden = nullptr; + if (SkipBody && !hasVisibleDefinition(Def, &Hidden)) { + *SkipBody = 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); + return Def; + } + Diag(NameLoc, diag::err_redefinition) << Name; Diag(Def->getLocation(), diag::note_previous_definition); // FIXME: Would it make sense to try to "forget" the previous diff --git a/test/Modules/Inputs/submodules-merge-defs/defs.h b/test/Modules/Inputs/submodules-merge-defs/defs.h index 1c866b55aa..dfb58adcbc 100644 --- a/test/Modules/Inputs/submodules-merge-defs/defs.h +++ b/test/Modules/Inputs/submodules-merge-defs/defs.h @@ -1,6 +1,16 @@ -struct A {}; +struct A { int a_member; }; +namespace { inline int use_a(A a) { return a.a_member; } } + class B { struct Inner1 {}; +public: struct Inner2; }; +// Check that lookup and access checks are performed in the right context. struct B::Inner2 : Inner1 {}; + +// Check that base-specifiers are correctly disambiguated. +template struct C_Base { struct D { constexpr operator int() const { return 0; } }; }; +const int C_Const = 0; +struct C1 : C_Base::D{}> {} extern c1; +struct C2 : C_Base::D{} extern c2; diff --git a/test/Modules/submodules-merge-defs.cpp b/test/Modules/submodules-merge-defs.cpp index 48d4feb7cf..9b5b13d63f 100644 --- a/test/Modules/submodules-merge-defs.cpp +++ b/test/Modules/submodules-merge-defs.cpp @@ -1,5 +1,5 @@ // 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 +// RUN: %clang_cc1 -x c++ -std=c++11 -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" @@ -7,7 +7,22 @@ A pre_a; // expected-error {{must be imported}} expected-error {{must use 'struct'}} // expected-note@defs.h:1 {{here}} +B::Inner2 pre_bi; // expected-error +{{must be imported}} +// expected-note@defs.h:4 +{{here}} +// expected-note@defs.h:10 +{{here}} + +C_Base<1> pre_cb1; // expected-error +{{must be imported}} +// expected-note@defs.h:13 +{{here}} +C1 pre_c1; // expected-error +{{must be imported}} expected-error {{must use 'struct'}} +// expected-note@defs.h:15 +{{here}} +C2 pre_c2; // expected-error +{{must be imported}} expected-error {{must use 'struct'}} +// expected-note@defs.h:16 +{{here}} + // Make definitions from second module visible. #include "import-and-redefine.h" A post_a; +B::Inner2 post_bi; +C_Base<1> post_cb1; +C1 c1; +C2 c2;