Don't instantiate lambda closure types in default member initializers
authorRichard Smith <richard@metafoo.co.uk>
Wed, 21 Oct 2020 00:35:15 +0000 (17:35 -0700)
committerRichard Smith <richard@metafoo.co.uk>
Wed, 21 Oct 2020 00:37:07 +0000 (17:37 -0700)
when instantiating the enclosing class.

We'll build new lambda closure types if and when we instantiate the
default member initializer, and instantiating the closure type by itself
can go wrong in cases where we fully-instantiate nested classes (in
explicit instantiations of the enclosing class and when the enclosing
class is a local class) -- we will instantiate the 'operator()' as a
regular function rather than as a lambda call operator, so it doesn't
get to use its captures, has the wrong 'this' type, etc.

clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/test/SemaTemplate/explicit-instantiation.cpp
clang/test/SemaTemplate/instantiate-local-class.cpp

index 2e96eaa..a131188 100644 (file)
@@ -2696,7 +2696,10 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
 
     // BlockDecls can appear in a default-member-initializer. They must be the
     // child of a BlockExpr, so we only know how to instantiate them from there.
-    if (isa<BlockDecl>(Member))
+    // Similarly, lambda closure types are recreated when instantiating the
+    // corresponding LambdaExpr.
+    if (isa<BlockDecl>(Member) ||
+        (isa<CXXRecordDecl>(Member) && cast<CXXRecordDecl>(Member)->isLambda()))
       continue;
 
     if (Member->isInvalidDecl()) {
index d88c50b..73f07ef 100644 (file)
@@ -179,4 +179,10 @@ struct B : A<0> {
   virtual void foo() override; // expected-error{{declaration of 'foo' overrides a 'final' function}}
 };
 }
+
+template<typename T> struct LambdaInDefaultMemberInitInExplicitInstantiation {
+  int a = [this] { return a; }();
+};
+template struct LambdaInDefaultMemberInitInExplicitInstantiation<int>;
+LambdaInDefaultMemberInitInExplicitInstantiation<float> x;
 #endif
index 15b455f..65caaa1 100644 (file)
@@ -495,3 +495,12 @@ namespace PR45000 {
   void g() { f<int>(); }
   // expected-note@-1 {{in instantiation of default function argument expression for 'f<int>' required here}}
 }
+
+namespace LambdaInDefaultMemberInitializer {
+  template<typename T> void f() {
+    struct S {
+      void *p = [this] { return &p; }();
+    };
+  }
+  template void f<int>();
+}