[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
This commit is contained in:
Richard Smith 2016-01-09 06:58:48 +00:00
parent 72cc170915
commit b3db27cd07
2 changed files with 35 additions and 17 deletions

View File

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

View File

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