From: Richard Smith Date: Sat, 9 Jan 2016 06:58:48 +0000 (+0000) Subject: [modules] If we're treating an elaborated-type-specifier as if it introduces a X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b3db27cd07f9e62076df5f571b359f9e90f779ec;p=clang [modules] If we're treating an elaborated-type-specifier as if it introduces a tag (because the previous declaration was found in a different module), inject the tag into the appropriate scope (that is, the enclosing scope if we're in a function prototype scope in C++). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@257251 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 645074deaa..0864984c06 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -11798,6 +11798,28 @@ static bool isAcceptableTagRedeclContext(Sema &S, DeclContext *OldDC, return false; } +/// Find the DeclContext in which a tag is implicitly declared if we see an +/// elaborated type specifier in the specified context, and lookup finds +/// nothing. +static DeclContext *getTagInjectionContext(DeclContext *DC) { + while (!DC->isFileContext() && !DC->isFunctionOrMethod()) + DC = DC->getParent(); + return DC; +} + +/// Find the Scope in which a tag is implicitly declared if we see an +/// elaborated type specifier in the specified context, and lookup finds +/// nothing. +static Scope *getTagInjectionScope(Scope *S, const LangOptions &LangOpts) { + while (S->isClassScope() || + (LangOpts.CPlusPlus && + S->isFunctionPrototypeScope()) || + ((S->getFlags() & Scope::DeclScope) == 0) || + (S->getEntity() && S->getEntity()->isTransparentContext())) + S = S->getParent(); + return S; +} + /// \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. /// TagSpec indicates what kind of tag this is. TUK indicates whether this is a @@ -12114,16 +12136,10 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // Find the context where we'll be declaring the tag. // FIXME: We would like to maintain the current DeclContext as the // lexical context, - while (!SearchDC->isFileContext() && !SearchDC->isFunctionOrMethod()) - SearchDC = SearchDC->getParent(); + SearchDC = getTagInjectionContext(SearchDC); // Find the scope where we'll be declaring the tag. - while (S->isClassScope() || - (getLangOpts().CPlusPlus && - S->isFunctionPrototypeScope()) || - ((S->getFlags() & Scope::DeclScope) == 0) || - (S->getEntity() && S->getEntity()->isTransparentContext())) - S = S->getParent(); + S = getTagInjectionScope(S, getLangOpts()); } else { assert(TUK == TUK_Friend); // C++ [namespace.memdef]p3: @@ -12293,14 +12309,13 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // the declaration would have meant the same thing if no prior // declaration were found, that is, if it was found in the same // scope where we would have injected a declaration. - DeclContext *InjectedDC = CurContext; - while (!InjectedDC->isFileContext() && - !InjectedDC->isFunctionOrMethod()) - InjectedDC = InjectedDC->getParent(); - if (!InjectedDC->getRedeclContext()->Equals( - PrevDecl->getDeclContext()->getRedeclContext())) + if (!getTagInjectionContext(CurContext) + ->getRedeclContext() + ->Equals(PrevDecl->getDeclContext()->getRedeclContext())) return PrevTagDecl; - // This is in the injected scope, create a new declaration. + // This is in the injected scope, create a new declaration in + // that scope. + S = getTagInjectionScope(S, getLangOpts()); } else { return PrevTagDecl; } diff --git a/test/Modules/tag-injection.cpp b/test/Modules/tag-injection.cpp index 75c8b5fecd..e55598b062 100644 --- a/test/Modules/tag-injection.cpp +++ b/test/Modules/tag-injection.cpp @@ -1,12 +1,15 @@ // RUN: rm -rf %t // RUN: mkdir %t -// RUN: touch %t/a.h -// RUN: echo 'struct X {};' > %t/b.h +// RUN: echo 'struct tm;' > %t/a.h +// RUN: echo 'struct X {}; void foo(struct tm*);' > %t/b.h // RUN: echo 'module X { module a { header "a.h" } module b { header "b.h" } }' > %t/x.modulemap +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -x c++ -fmodule-map-file=%t/x.modulemap %s -I%t -verify -std=c++11 // RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -x c++ -fmodule-map-file=%t/x.modulemap %s -I%t -verify -fmodules-local-submodule-visibility -std=c++11 #include "a.h" +using ::tm; + struct A { // This use of 'struct X' makes the declaration (but not definition) of X visible. virtual void f(struct X *p);