summaryrefslogtreecommitdiffstats
path: root/clang/lib/Sema/SemaDecl.cpp
diff options
context:
space:
mode:
authorReid Kleckner <rnk@google.com>2016-12-14 17:44:11 +0000
committerReid Kleckner <rnk@google.com>2016-12-14 17:44:11 +0000
commit34a0f3dc2fcfec0ef5234407f0ea4cd473a6c166 (patch)
tree1d47638130c91626f3d83d54c39616bcd4b999ad /clang/lib/Sema/SemaDecl.cpp
parent23025f8483f9aeab813d5e5d25ceb94e4ac95b3b (diff)
downloadbcm5719-llvm-34a0f3dc2fcfec0ef5234407f0ea4cd473a6c166.tar.gz
bcm5719-llvm-34a0f3dc2fcfec0ef5234407f0ea4cd473a6c166.zip
Improve our handling of tag decls in function prototypes
r289225 broke AST invariants by reparenting enumerators into function decl contexts. This improves things by only reparenting TagDecls while also attempting to preserve the lexical declcontext chain. The interesting example here is: int f(struct S { enum E { a = 1 } b; } c); The semantic contexts of E and S should be f, and the lexical context of S should be f and the lexical context of E should be S. We didn't do that with r289225, but now we should. This change should also improve our behavior on this example: void f() { extern void ext(struct S { } o); // S injected here } Before r289225 we would only remove 'S' from the surrounding tag injection context if it was the TU, but now we properly reparent S from f to ext. Fixes PR31366 llvm-svn: 289678
Diffstat (limited to 'clang/lib/Sema/SemaDecl.cpp')
-rw-r--r--clang/lib/Sema/SemaDecl.cpp82
1 files changed, 52 insertions, 30 deletions
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 692853fd146..89cb4398638 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7771,6 +7771,28 @@ static void checkIsValidOpenCLKernelParameter(
} while (!VisitStack.empty());
}
+/// 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;
+}
+
NamedDecl*
Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
TypeSourceInfo *TInfo, LookupResult &Previous,
@@ -8247,15 +8269,37 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
}
if (!getLangOpts().CPlusPlus) {
- // In C, find all the non-parameter declarations from the prototype and
- // move them into the new function decl context as well. Typically they
- // will have been added to the surrounding context of the prototype.
+ // In C, find all the tag declarations from the prototype and move them
+ // into the function DeclContext. Remove them from the surrounding tag
+ // injection context of the function, which is typically but not always
+ // the TU.
+ DeclContext *PrototypeTagContext =
+ getTagInjectionContext(NewFD->getLexicalDeclContext());
for (NamedDecl *NonParmDecl : FTI.getDeclsInPrototype()) {
- DeclContext *OldDC = NonParmDecl->getDeclContext();
- if (OldDC->containsDecl(NonParmDecl))
- OldDC->removeDecl(NonParmDecl);
- NonParmDecl->setDeclContext(NewFD);
- NewFD->addDecl(NonParmDecl);
+ auto *TD = dyn_cast<TagDecl>(NonParmDecl);
+
+ // We don't want to reparent enumerators. Look at their parent enum
+ // instead.
+ if (!TD) {
+ if (auto *ECD = dyn_cast<EnumConstantDecl>(NonParmDecl))
+ TD = cast<EnumDecl>(ECD->getDeclContext());
+ }
+ if (!TD)
+ continue;
+ DeclContext *TagDC = TD->getLexicalDeclContext();
+ if (!TagDC->containsDecl(TD))
+ continue;
+ TagDC->removeDecl(TD);
+ TD->setDeclContext(NewFD);
+ NewFD->addDecl(TD);
+
+ // Preserve the lexical DeclContext if it is not the surrounding tag
+ // injection context of the FD. In this example, the semantic context of
+ // E will be f and the lexical context will be S, while both the
+ // semantic and lexical contexts of S will be f:
+ // void f(struct S { enum E { a } f; } s);
+ if (TagDC != PrototypeTagContext)
+ TD->setLexicalDeclContext(TagDC);
}
}
} else if (const FunctionProtoType *FT = R->getAs<FunctionProtoType>()) {
@@ -12632,28 +12676,6 @@ 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
OpenPOWER on IntegriCloud