aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Trieu <rtrieu@google.com>2018-01-19 20:46:19 +0000
committerRichard Trieu <rtrieu@google.com>2018-01-19 20:46:19 +0000
commitb340925d70563557d532c699f7b1b681fb621dfe (patch)
tree877331138b99c5a6f687a63368b328b6cc6bf284
parentf11660ab09526b0c35d6009ecf0b7283f87d0cc5 (diff)
Allow BlockDecl in CXXRecord scope to have no access specifier.
Using a BlockDecl in a default member initializer causes it to be attached to CXXMethodDecl without its access specifier being set. This prevents a crash where getAccess is called on this BlockDecl, since that method expects any Decl in CXXRecord scope to have an access specifier. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@322984 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/AST/DeclBase.cpp2
-rw-r--r--test/Modules/odr_hash-blocks.cpp119
2 files changed, 121 insertions, 0 deletions
diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp
index fe8bd92f27..18aa53b857 100644
--- a/lib/AST/DeclBase.cpp
+++ b/lib/AST/DeclBase.cpp
@@ -891,12 +891,14 @@ bool Decl::AccessDeclContextSanity() const {
// 4. the context is not a record
// 5. it's invalid
// 6. it's a C++0x static_assert.
+ // 7. it's a block literal declaration
if (isa<TranslationUnitDecl>(this) ||
isa<TemplateTypeParmDecl>(this) ||
isa<NonTypeTemplateParmDecl>(this) ||
!isa<CXXRecordDecl>(getDeclContext()) ||
isInvalidDecl() ||
isa<StaticAssertDecl>(this) ||
+ isa<BlockDecl>(this) ||
// FIXME: a ParmVarDecl can have ClassTemplateSpecialization
// as DeclContext (?).
isa<ParmVarDecl>(this) ||
diff --git a/test/Modules/odr_hash-blocks.cpp b/test/Modules/odr_hash-blocks.cpp
new file mode 100644
index 0000000000..07dfa4ce2a
--- /dev/null
+++ b/test/Modules/odr_hash-blocks.cpp
@@ -0,0 +1,119 @@
+// Clear and create directories
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: mkdir %t/cache
+// RUN: mkdir %t/Inputs
+
+// Build first header file
+// RUN: echo "#define FIRST" >> %t/Inputs/first.h
+// RUN: cat %s >> %t/Inputs/first.h
+
+// Build second header file
+// RUN: echo "#define SECOND" >> %t/Inputs/second.h
+// RUN: cat %s >> %t/Inputs/second.h
+
+// Test that each header can compile
+// RUN: %clang_cc1 -fsyntax-only -x c++ -std=c++11 -fblocks %t/Inputs/first.h
+// RUN: %clang_cc1 -fsyntax-only -x c++ -std=c++11 -fblocks %t/Inputs/second.h
+
+// Build module map file
+// RUN: echo "module FirstModule {" >> %t/Inputs/module.map
+// RUN: echo " header \"first.h\"" >> %t/Inputs/module.map
+// RUN: echo "}" >> %t/Inputs/module.map
+// RUN: echo "module SecondModule {" >> %t/Inputs/module.map
+// RUN: echo " header \"second.h\"" >> %t/Inputs/module.map
+// RUN: echo "}" >> %t/Inputs/module.map
+
+// Run test
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps \
+// RUN: -fmodules-cache-path=%t/cache -x c++ -I%t/Inputs \
+// RUN: -verify %s -std=c++11 -fblocks
+
+#if !defined(FIRST) && !defined(SECOND)
+#include "first.h"
+#include "second.h"
+#endif
+
+// Used for testing
+#if defined(FIRST)
+#define ACCESS public:
+#elif defined(SECOND)
+#define ACCESS private:
+#endif
+
+// TODO: S1, S2, and S3 should generate errors.
+namespace Blocks {
+#if defined(FIRST)
+struct S1 {
+ void (^block)(int x) = ^(int x) { };
+};
+#elif defined(SECOND)
+struct S1 {
+ void (^block)(int x) = ^(int y) { };
+};
+#else
+S1 s1;
+#endif
+
+#if defined(FIRST)
+struct S2 {
+ int (^block)(int x) = ^(int x) { return x + 1; };
+};
+#elif defined(SECOND)
+struct S2 {
+ int (^block)(int x) = ^(int x) { return x; };
+};
+#else
+S2 s2;
+#endif
+
+#if defined(FIRST)
+struct S3 {
+ void run(int (^block)(int x));
+};
+#elif defined(SECOND)
+struct S3 {
+ void run(int (^block)(int x, int y));
+};
+#else
+S3 s3;
+#endif
+
+#define DECLS \
+ int (^block)(int x) = ^(int x) { return x + x; }; \
+ void run(int (^block)(int x, int y));
+
+#if defined(FIRST) || defined(SECOND)
+struct Valid1 {
+ DECLS
+};
+#else
+Valid1 v1;
+#endif
+
+#if defined(FIRST) || defined(SECOND)
+struct Invalid1 {
+ DECLS
+ ACCESS
+};
+#else
+Invalid1 i1;
+// expected-error@second.h:* {{'Blocks::Invalid1' has different definitions in different modules; first difference is definition in module 'SecondModule' found private access specifier}}
+// expected-note@first.h:* {{but in 'FirstModule' found public access specifier}}
+#endif
+
+#undef DECLS
+}
+
+// Keep macros contained to one file.
+#ifdef FIRST
+#undef FIRST
+#endif
+
+#ifdef SECOND
+#undef SECOND
+#endif
+
+#ifdef ACCESS
+#undef ACCESS
+#endif