]> granicus.if.org Git - clang/commitdiff
Lazily load the ContextDecl for a lambda's DefinitionData, to fix a
authorRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 25 Aug 2016 00:34:00 +0000 (00:34 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 25 Aug 2016 00:34:00 +0000 (00:34 +0000)
deserialization cycle caused by the ContextDecl recursively importing members
of the lambda's closure type.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@279694 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/DeclCXX.h
lib/AST/DeclCXX.cpp
lib/Serialization/ASTReaderDecl.cpp
lib/Serialization/ASTWriter.cpp
test/Modules/lambda-context.cpp [new file with mode: 0644]
test/PCH/cxx11-lambdas.mm

index 2071c1b0e1dcfb55e68b4aa351777dba61d48e9a..f2b798b5a35ac8a4d8dc69ed5d7171e84a1fcac9 100644 (file)
@@ -571,7 +571,7 @@ class CXXRecordDecl : public RecordDecl {
     /// actual DeclContext does not suffice. This is used for lambdas that
     /// occur within default arguments of function parameters within the class
     /// or within a data member initializer.
-    Decl *ContextDecl;
+    LazyDeclPtr ContextDecl;
     
     /// \brief The list of captures, both explicit and implicit, for this 
     /// lambda.
@@ -1673,10 +1673,7 @@ public:
   /// the declaration in which the lambda occurs, e.g., the function parameter 
   /// or the non-static data member. Otherwise, it returns NULL to imply that
   /// the declaration context suffices.
-  Decl *getLambdaContextDecl() const {
-    assert(isLambda() && "Not a lambda closure type!");
-    return getLambdaData().ContextDecl;    
-  }
+  Decl *getLambdaContextDecl() const;
   
   /// \brief Set the mangling number and context declaration for a lambda
   /// class.
index 7395db586be5aaa25e481c476d49fac594577e61..5256eda24bdd9d7cba0275dab4f193da412b0398 100644 (file)
@@ -1107,6 +1107,12 @@ CXXRecordDecl::getGenericLambdaTemplateParameterList() const {
   return nullptr;
 }
 
+Decl *CXXRecordDecl::getLambdaContextDecl() const {
+  assert(isLambda() && "Not a lambda closure type!");
+  ExternalASTSource *Source = getParentASTContext().getExternalSource();
+  return getLambdaData().ContextDecl.get(Source);
+}
+
 static CanQualType GetConversionType(ASTContext &Context, NamedDecl *Conv) {
   QualType T =
       cast<CXXConversionDecl>(Conv->getUnderlyingDecl()->getAsFunction())
index 04231cd8e0b714599f7ffab0280c36cc65842dbd..e00c9561cd876e154eeb680cb5ced3ee27ce9be2 100644 (file)
@@ -1539,7 +1539,7 @@ void ASTDeclReader::ReadCXXDefinitionData(
     Lambda.NumCaptures = Record[Idx++];
     Lambda.NumExplicitCaptures = Record[Idx++];
     Lambda.ManglingNumber = Record[Idx++];
-    Lambda.ContextDecl = ReadDecl(Record, Idx);
+    Lambda.ContextDecl = ReadDeclID(Record, Idx);
     Lambda.Captures 
       = (Capture*)Reader.Context.Allocate(sizeof(Capture)*Lambda.NumCaptures);
     Capture *ToCapture = Lambda.Captures;
index 72b2bf6d1689d24ff40750515733f0d4e92cc5c3..7f9ae8df1cdc7e348d8e9511c73066abfd58f432 100644 (file)
@@ -5565,7 +5565,7 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) {
     Record->push_back(Lambda.NumCaptures);
     Record->push_back(Lambda.NumExplicitCaptures);
     Record->push_back(Lambda.ManglingNumber);
-    AddDeclRef(Lambda.ContextDecl);
+    AddDeclRef(D->getLambdaContextDecl());
     AddTypeSourceInfo(Lambda.MethodTyInfo);
     for (unsigned I = 0, N = Lambda.NumCaptures; I != N; ++I) {
       const LambdaCapture &Capture = Lambda.Captures[I];
diff --git a/test/Modules/lambda-context.cpp b/test/Modules/lambda-context.cpp
new file mode 100644 (file)
index 0000000..6ce482c
--- /dev/null
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -fmodules -std=c++11 -emit-pch -o %t %s
+// RUN: %clang_cc1 -fmodules -std=c++11 -include-pch %t %s -verify
+//
+// This test checks for a bug in the deserialization code that was only
+// reachable with modules enabled, but actually building and using modules is
+// not necessary in order to trigger it, so we just use PCH here to make the
+// test simpler.
+
+#ifndef HEADER_INCLUDED
+#define HEADER_INCLUDED
+
+struct X { template <typename T> X(T) {} };
+struct Y { Y(X x = [] {}); };
+
+#else
+
+// This triggers us to load the specialization of X::X for Y's lambda. That
+// lambda's context decl must not be loaded as a result of loading the lambda,
+// as that would hit a deserialization cycle.
+X x = [] {}; // expected-no-diagnostics
+
+#endif
index 5d3323a02cff2153331edd29751578feb55eac04..1f568f05e11a217eafe8c2c8e80c47cb2b3d797c 100644 (file)
@@ -38,6 +38,11 @@ int init_capture(T t) {
   return [&, x(t)] { return sizeof(x); };
 }
 
+struct X {
+  template <typename T> X(T);
+};
+struct Y { Y(const X &x = [] {}); };
+
 #else
 
 // CHECK-PRINT: T add_slowly
@@ -54,4 +59,6 @@ int add(int x, int y) {
 // CHECK-PRINT: init_capture
 // CHECK-PRINT: [&, x(t)]
 
+X x = [] {};
+
 #endif