vsk created this revision.
vsk added reviewers: rsmith, erichkeane.

When it's not possible to initialize an implicit capture, add a note
pointing to the first use of the captured variable.

Example (the `note` is new):

lambda-expressions.cpp:81:15: error: no matching constructor for initialization 
of 'G'

  return [=]{
          ^

lambda-expressions.cpp:82:24: note: implicitly capturing 'g', first used here

  const G* gg = &g;
                 ^

As suggested in https://reviews.llvm.org/D50927.


https://reviews.llvm.org/D52064

Files:
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Sema/Sema.h
  clang/lib/Frontend/FrontendActions.cpp
  clang/lib/Sema/SemaLambda.cpp
  clang/lib/Sema/SemaTemplateInstantiate.cpp
  clang/test/CXX/expr/expr.prim/expr.prim.lambda/p14.cpp
  clang/test/SemaCXX/lambda-expressions.cpp

Index: clang/test/SemaCXX/lambda-expressions.cpp
===================================================================
--- clang/test/SemaCXX/lambda-expressions.cpp
+++ clang/test/SemaCXX/lambda-expressions.cpp
@@ -77,8 +77,18 @@
     struct G { G(); G(G&); int a; }; // expected-note 6 {{not viable}}
     G g;
     [=]() { const G* gg = &g; return gg->a; };
-    [=]() { return [=]{ const G* gg = &g; return gg->a; }(); }; // expected-error {{no matching constructor for initialization of 'G'}}
-    (void)^{ return [=]{ const G* gg = &g; return gg->a; }(); }; // expected-error 2 {{no matching constructor for initialization of 'const G'}}
+    [=]() {
+      return [=]{ // expected-error {{no matching constructor for initialization of 'G'}}
+        const G* gg = &g; // expected-note {{implicitly capturing 'g', first used here}}
+        return gg->a;
+      }();
+    };
+    (void)^{ // expected-error@+1 {{no matching constructor for initialization of 'const G'}}
+      return [=]{ // expected-error@+1 {{no matching constructor for initialization of 'const G'}}
+        const G* gg = &g; // expected-note {{implicitly capturing 'g', first used here}}
+        return gg->a;
+      }();
+    };
 
     const int h = a; // expected-note {{declared}}
     []() { return h; }; // expected-error {{variable 'h' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note {{lambda expression begins here}}
@@ -378,7 +388,9 @@
 namespace PR18473 {
   template<typename T> void f() {
     T t(0);
-    (void) [=]{ int n = t; }; // expected-error {{deleted}}
+    (void)[=] { // expected-error {{call to deleted constructor of 'PR18473::NoCopy'}}
+      int n = t; // expected-note {{implicitly capturing 't', first used here}}
+    };
   }
 
   template void f<int>();
Index: clang/test/CXX/expr/expr.prim/expr.prim.lambda/p14.cpp
===================================================================
--- clang/test/CXX/expr/expr.prim/expr.prim.lambda/p14.cpp
+++ clang/test/CXX/expr/expr.prim/expr.prim.lambda/p14.cpp
@@ -16,7 +16,7 @@
 void capture_by_copy(NonCopyable nc, NonCopyable &ncr, const NonConstCopy nco) {
   (void)[nc] { }; // expected-error{{capture of variable 'nc' as type 'NonCopyable' calls private copy constructor}}
   (void)[=] { // expected-error{{capture of variable 'ncr' as type 'NonCopyable' calls private copy constructor}}
-    ncr.foo();
+    ncr.foo(); // expected-note{{implicitly capturing 'ncr', first used here}}
   }();
 
   [nco] {}(); // expected-error{{no matching constructor for initialization of 'const NonConstCopy'}}
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -200,6 +200,7 @@
   case DeclaringSpecialMember:
   case DefiningSynthesizedFunction:
   case ExceptionSpecEvaluation:
+  case ImplicitLambdaCaptureInitialization:
     return false;
 
   // This function should never be called when Kind's value is Memoization.
@@ -653,6 +654,13 @@
       break;
     }
 
+    case CodeSynthesisContext::ImplicitLambdaCaptureInitialization: {
+      Diags.Report(Active->PointOfInstantiation,
+                   diag::note_implicitly_capturing_var_first_used_here)
+          << cast<NamedDecl>(Active->Entity);
+      break;
+    }
+
     case CodeSynthesisContext::Memoization:
       break;
     }
@@ -698,6 +706,7 @@
 
     case CodeSynthesisContext::DeclaringSpecialMember:
     case CodeSynthesisContext::DefiningSynthesizedFunction:
+    case CodeSynthesisContext::ImplicitLambdaCaptureInitialization:
       // This happens in a context unrelated to template instantiation, so
       // there is no SFINAE.
       return None;
Index: clang/lib/Sema/SemaLambda.cpp
===================================================================
--- clang/lib/Sema/SemaLambda.cpp
+++ clang/lib/Sema/SemaLambda.cpp
@@ -1401,6 +1401,14 @@
   SourceLocation Loc =
       IsImplicitCapture ? ImplicitCaptureLoc : Capture.getLocation();
 
+  if (IsImplicitCapture) {
+    Sema::CodeSynthesisContext Ctx;
+    Ctx.Entity = Var;
+    Ctx.Kind = Sema::CodeSynthesisContext::ImplicitLambdaCaptureInitialization;
+    Ctx.PointOfInstantiation = Capture.getLocation();
+    S.pushCodeSynthesisContext(Ctx);
+  }
+
   // C++11 [expr.prim.lambda]p21:
   //   When the lambda-expression is evaluated, the entities that
   //   are captured by copy are used to direct-initialize each
@@ -1423,7 +1431,12 @@
       Var->getIdentifier(), Field->getType(), Loc);
   InitializationKind InitKind = InitializationKind::CreateDirect(Loc, Loc, Loc);
   InitializationSequence Init(S, Entity, InitKind, Ref);
-  return Init.Perform(S, Entity, InitKind, Ref);
+  ExprResult Result = Init.Perform(S, Entity, InitKind, Ref);
+
+  if (IsImplicitCapture)
+    S.popCodeSynthesisContext();
+
+  return Result;
 }
 
 ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
Index: clang/lib/Frontend/FrontendActions.cpp
===================================================================
--- clang/lib/Frontend/FrontendActions.cpp
+++ clang/lib/Frontend/FrontendActions.cpp
@@ -349,6 +349,8 @@
       return "DeclaringSpecialMember";
     case CodeSynthesisContext::DefiningSynthesizedFunction:
       return "DefiningSynthesizedFunction";
+    case CodeSynthesisContext::ImplicitLambdaCaptureInitialization:
+      return "ImplicitLambdaCaptureInitialization";
     case CodeSynthesisContext::Memoization:
       return "Memoization";
     }
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -7178,7 +7178,10 @@
       /// Memoization means we are _not_ instantiating a template because
       /// it is already instantiated (but we entered a context where we
       /// would have had to if it was not already instantiated).
-      Memoization
+      Memoization,
+
+      /// We are initializing an implicit capture for a lambda.
+      ImplicitLambdaCaptureInitialization,
     } Kind;
 
     /// Was the enclosing context a non-instantiation SFINAE context?
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1765,6 +1765,10 @@
   "the function it overrides (class type %1 is more qualified than class "
   "type %2">;
 
+// C++ lambdas
+def note_implicitly_capturing_var_first_used_here : Note<
+  "implicitly capturing %0, first used here">;
+
 // C++ implicit special member functions
 def note_in_declaration_of_implicit_special_member : Note<
   "while declaring the implicit %sub{select_special_member_kind}1"
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D52064: [... Vedant Kumar via Phabricator via cfe-commits

Reply via email to