[PATCH] D122768: [Clang][C++20] Support capturing structured bindings in lambdas

Aaron Ballman via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 28 08:10:07 PDT 2022


aaron.ballman added inline comments.


================
Comment at: clang/include/clang/AST/Stmt.h:62-63
 class Token;
 class VarDecl;
+class ValueDecl;
 
----------------
We usually keep these alphabetical.


================
Comment at: clang/lib/AST/ExprCXX.cpp:1214-1216
+  return (C->capturesVariable() && isa<VarDecl>(C->getCapturedVar()) &&
+          cast<VarDecl>(C->getCapturedVar())->isInitCapture() &&
           (getCallOperator() == C->getCapturedVar()->getDeclContext()));
----------------
I think early returns help make this a bit more clear.


================
Comment at: clang/lib/AST/StmtPrinter.cpp:2167
     if (Node->isInitCapture(C)) {
-      VarDecl *D = C->getCapturedVar();
+      VarDecl *D = cast<VarDecl>(C->getCapturedVar());
 
----------------



================
Comment at: clang/lib/Analysis/AnalysisDeclContext.cpp:173
+    ValueDecl *VD = LC.getCapturedVar();
+    if (isSelfDecl(dyn_cast<VarDecl>(VD)))
       return dyn_cast<ImplicitParamDecl>(VD);
----------------
This looks dangerous -- `isSelfDecl()` uses the pointer variable in ways that would be Bad News for null pointers.


================
Comment at: clang/lib/CodeGen/CGOpenMPRuntime.cpp:9060
         continue;
-      const VarDecl *VD = LC.getCapturedVar();
+      const VarDecl *VD = cast<VarDecl>(LC.getCapturedVar());
       if (LC.getCaptureKind() != LCK_ByRef && !VD->getType()->isPointerType())
----------------
cor3ntin wrote:
> cor3ntin wrote:
> > I'm not sure we currently prohibit capturing a structured binding in openmp, i should fix that
> I disabled the feature in OpenMP mode, as this needs more investigation
Ping @jdoerfert -- do you happen to know what's up here?


================
Comment at: clang/lib/Sema/ScopeInfo.cpp:223-224
   // init-capture.
-  return !isNested() && isVariableCapture() && getVariable()->isInitCapture();
+  return !isNested() && isVariableCapture() && isa<VarDecl>(getVariable()) &&
+         cast<VarDecl>(getVariable())->isInitCapture();
 }
----------------
This also has a bad code smell for `isa<>` followed by `cast<>`, but I'm not certain if the rewrite is actually better or not.


================
Comment at: clang/lib/Sema/SemaExpr.cpp:16393
 
-    VarDecl *Var = Cap.getVariable();
+    VarDecl *Var = cast<VarDecl>(Cap.getVariable());
     Expr *CopyExpr = nullptr;
----------------
Is `cast<>` safe here?


================
Comment at: clang/lib/Sema/SemaExpr.cpp:18324
+
+  assert((isa<VarDecl>(Var) || isa<BindingDecl>(Var)) &&
+         "Only variables and structured bindings can be captured");
----------------



================
Comment at: clang/lib/Sema/SemaExpr.cpp:18527
+  BindingDecl *BD = nullptr;
+  if (VarDecl *V = dyn_cast_or_null<VarDecl>(Var)) {
+    if (V->getInit())
----------------
I'm assuming, given that the function didn't used to accept nullptr for `Var` and the `else if` is using `dyn_cast<>`.


================
Comment at: clang/lib/Sema/SemaExpr.cpp:18531
+  } else if ((BD = dyn_cast<BindingDecl>(Var))) {
+    ME = dyn_cast_or_null<MemberExpr>(BD->getBinding());
+  }
----------------
Can this return nullptr?


================
Comment at: clang/lib/Sema/SemaExpr.cpp:18534-18535
+
+  // Capturing a bitfield by reference is not allowed
+  // except in OpenMP mode
+  if (ByRef && ME &&
----------------



================
Comment at: clang/lib/Sema/SemaExpr.cpp:18539
+       !S.isOpenMPCapturedDecl(Var))) {
+    FieldDecl *FD = dyn_cast_or_null<FieldDecl>(ME->getMemberDecl());
+    if (FD &&
----------------
Can this return nullptr?


================
Comment at: clang/lib/Sema/SemaExpr.cpp:18551
+  }
+  // FIXME: We should support capturing structured bindings in OpenMP
+  if (!Invalid && BD && S.LangOpts.OpenMP) {
----------------



================
Comment at: clang/lib/Sema/SemaExpr.cpp:18749
   DeclContext *VarDC = Var->getDeclContext();
-  if (Var->isInitCapture())
-    VarDC = VarDC->getParent();
+  VarDecl *VD = dyn_cast<VarDecl>(Var);
+  if (VD) {
----------------



================
Comment at: clang/lib/Sema/SemaExpr.cpp:18750-18756
+  if (VD) {
+    if (VD && VD->isInitCapture())
+      VarDC = VarDC->getParent();
+  } else {
+    VD = dyn_cast<DecompositionDecl>(
+        cast<BindingDecl>(Var)->getDecomposedDecl());
+  }
----------------



================
Comment at: clang/lib/Sema/SemaInit.cpp:7851
+        bool InitCapture =
+            isa<VarDecl>(VD) && cast<VarDecl>(VD)->isInitCapture();
         Diag(Elem.Capture->getLocation(), diag::note_lambda_capture_initializer)
----------------
shafik wrote:
> cor3ntin wrote:
> > shafik wrote:
> > > I see we are doing this kind of check to see if we have a `VarDecl` and then check if it is an init capture and I wish there was a way not to repeat this but I don't see it.
> > I considered having a function In ValueDecl, what do you think?
> I thought about that but when I looked at `ValueDecl` it seemed pretty lightweight.
> 
> @aaron.ballman wdyt is `ValueDecl` the right place or is this code repetition ok? 
I think it makes sense to sink this into `ValueDecl` given how often it's come up. We really try to discourage people from doing `isa<>` followed by a `cast<>` as that's a bad code smell for `dyn_cast<>`.


================
Comment at: clang/lib/Sema/SemaLambda.cpp:1169
 
-      Var = R.getAsSingle<VarDecl>();
+      if (auto BD = R.getAsSingle<BindingDecl>())
+        Var = BD;
----------------



================
Comment at: clang/lib/Sema/SemaLambda.cpp:1208-1209
+    if (isa<BindingDecl>(Var)) {
+      Underlying = dyn_cast_or_null<VarDecl>(
+          cast<BindingDecl>(Var)->getDecomposedDecl());
+    } else
----------------
Does `getDecomposedDecl()` ever actually return nullptr?


================
Comment at: clang/lib/Sema/SemaLambda.cpp:1717
   if (Capture.isVariableCapture()) {
-    auto *Var = Capture.getVariable();
-    if (Var->isInitCapture())
-      TSI = Capture.getVariable()->getTypeSourceInfo();
+    VarDecl *Var = llvm::dyn_cast_or_null<VarDecl>(Capture.getVariable());
+    if (Var && Var->isInitCapture())
----------------



================
Comment at: clang/lib/Sema/TreeTransform.h:12989
     TransformedInitCapture &Result = InitCaptures[C - E->capture_begin()];
-    VarDecl *OldVD = C->getCapturedVar();
+    VarDecl *OldVD = cast<VarDecl>(C->getCapturedVar());
 
----------------



================
Comment at: clang/lib/Sema/TreeTransform.h:13174
 
-      VarDecl *OldVD = C->getCapturedVar();
+      VarDecl *OldVD = cast<VarDecl>(C->getCapturedVar());
       llvm::SmallVector<Decl*, 4> NewVDs;
----------------
I'll stop commenting on these -- you should take a pass over your changes and use `const auto *`` or `auto *` anywhere the type is spelled on the RHS via `cast<>`, `dyn_cast<>`, or similar.


================
Comment at: clang/www/cxx_status.html:1143
       <td><a href="https://wg21.link/p1091r3">P1091R3</a></td>
-      <td rowspan="2" class="partial" align="center">Partial</td>
+      <td rowspan="2" class="unreleased" align="center">Clang 15</td>
     </tr>
----------------



Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D122768/new/

https://reviews.llvm.org/D122768



More information about the cfe-commits mailing list