[PATCH] D24639: [Sema] Warn when returning a lambda that captures a local variable by reference

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Thu Sep 15 18:39:50 PDT 2016


rsmith added a comment.

> But not here, because we would have to verify that the pointer in lam wasn't mutated in a previous call of the lambda:


Isn't that guaranteed, because the lambda is not marked `mutable`?


================
Comment at: lib/Sema/SemaChecking.cpp:6594
@@ -6590,1 +6593,3 @@
+    if (LambdaDecl->isLambda())
+      stackE = EvalVal(RetValExp, refVars, /*ParentDecl=*/nullptr);
   }
----------------
I don't think it makes sense to use `EvalVal` for this. `EvalVal` is intended to handle the case of a glvalue that is being returned from the function, which isn't quite the same thing you want to check here -- I think you would be better off instead directly checking the captures of the lambda from here (`LambdaDecl` can give you a list) rather than calling this.

It also seems like the `RetValExp` doesn't actually matter at all here in most cases. If the closure type captures a local by reference, we should warn, regardless of the initialization expression, and likewise if it's not `mutable` and captures a local by address through an init-capture. For `mutable` lambdas with init-captures that capture by address, perhaps we could do a quick localized walk here on the `RetValExp` to see if it's returned directly.

================
Comment at: lib/Sema/SemaChecking.cpp:6845
@@ -6832,3 +6844,3 @@
                            const Decl *ParentDecl) {
-  do {
+  for (;;) {
     // We should only be called for evaluating non-pointer expressions, or
----------------
We prefer `while (true)` over `for (;;)` (but I agree the existing `do ... while (true);` is terrible).

================
Comment at: lib/Sema/SemaChecking.cpp:6886-6887
@@ -6873,3 +6885,4 @@
         if (V->hasLocalStorage()) {
-          if (!V->getType()->isReferenceType())
+          auto *RD = V->getType()->getAsCXXRecordDecl();
+          if (!V->getType()->isReferenceType() && !(RD && RD->isLambda()))
             return DR;
----------------
This is the kind of problem I was worried about. Given:

  auto &f() {
    auto lam = [] {};
    return lam;
  }

... we'll currently diagnose that we're returning the address of a local, from here. But with this change applied, we lose that diagnostic because `EvalVal` can't tell the difference between this case (returning by reference) and returning the lambda by value.

================
Comment at: test/SemaCXX/cxx1y-generic-lambdas-capturing.cpp:185
@@ -184,3 +184,3 @@
           f(y, d);
-          f(z, d);
+          f(z, d); // expected-warning {{address of stack memory associated with local variable 'z' returned}}
           decltype(a) A = a;
----------------
This diagnostic looks like it'll be confusing. Can you add a note pointing at the `return` statement in question (we're warning about the one on line 179, I assume)?

================
Comment at: test/SemaCXX/cxx1y-init-captures.cpp:123-124
@@ -122,3 +122,4 @@
       };
-      return M;
+      // FIXME: We shouldn't emit this warning here.
+      return M; // expected-warning{{stack memory}}
     };
----------------
Do you know why this happens? It looks like `EvalVal` should bail out on the `DeclRefExpr` in the init-capture, because it `refersToEnclosingVariableOrCapture`.

================
Comment at: test/SemaCXX/return-lambda-stack-addr.cpp:68-72
@@ +67,7 @@
+
+auto ref_ref() {
+  int x;
+  int &y = x;              // expected-note{{binding reference variable 'y' here}}
+  return [&] { (void)y; }; // expected-warning{{address of stack memory associated with local variable 'x' returned}}
+}
+
----------------
I'd like to see another testcase for the case when a local reference is safely captured by reference:

  auto ref_ref_ok(int &r) {
    int &y = r;
    return [&] { return y; }; // no warning
  }


https://reviews.llvm.org/D24639





More information about the cfe-commits mailing list