Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang] Fix a crash when a variable is captured by a block nested inside a lambda #93749

Merged
merged 4 commits into from
May 30, 2024

Conversation

ahatanak
Copy link
Collaborator

Eval->Value.get returns a null pointer when the variable doesn't have an initializer.

This fixes #93625.

rdar://128482541

`Eval->Value.get` returns a null pointer when the variable doesn't have
an initializer.

This fixes llvm#93625.

rdar://128482541
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels May 29, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented May 29, 2024

@llvm/pr-subscribers-clang

Author: Akira Hatanaka (ahatanak)

Changes

Eval->Value.get returns a null pointer when the variable doesn't have an initializer.

This fixes #93625.

rdar://128482541


Full diff: https://github.com/llvm/llvm-project/pull/93749.diff

2 Files Affected:

  • (modified) clang/lib/AST/Decl.cpp (+5-3)
  • (modified) clang/test/SemaObjCXX/block-capture.mm (+18)
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 41fbfe281ef65..4940a787e7d30 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2408,9 +2408,11 @@ Expr *VarDecl::getInit() {
     return cast<Expr>(S);
 
   auto *Eval = getEvaluatedStmt();
-  return cast<Expr>(Eval->Value.isOffset()
-                        ? Eval->Value.get(getASTContext().getExternalSource())
-                        : Eval->Value.get(nullptr));
+
+  return cast_or_null<Expr>(
+      Eval->Value.isOffset()
+          ? Eval->Value.get(getASTContext().getExternalSource())
+          : Eval->Value.get(nullptr));
 }
 
 Stmt **VarDecl::getInitAddress() {
diff --git a/clang/test/SemaObjCXX/block-capture.mm b/clang/test/SemaObjCXX/block-capture.mm
index 8ba02f919e015..231aef33f2c7e 100644
--- a/clang/test/SemaObjCXX/block-capture.mm
+++ b/clang/test/SemaObjCXX/block-capture.mm
@@ -83,3 +83,21 @@
   SubMove(SubSubMove &&);
 };
 TEST(SubMove);
+
+
+#if __cplusplus >= 202302L
+// clang used to crash compiling this code.
+namespace BlockInLambda {
+  struct S {
+    constexpr ~S();
+  };
+
+  void func(S const &a) {
+    [a](auto b) {
+      ^{
+        (void)a;
+      }();
+    }(12);
+  }
+}
+#endif

@ahatanak
Copy link
Collaborator Author

Note that the call to hasInit at the beginning of function VarDecl::getInit returns true even when the variable doesn't have an initializer if VarDecl::ensureEvaluatedStmt is called before that.

@fahadnayyar fahadnayyar self-requested a review May 29, 2024 23:39
Copy link
Contributor

@fahadnayyar fahadnayyar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

? Eval->Value.get(getASTContext().getExternalSource())
: Eval->Value.get(nullptr));

return cast_or_null<Expr>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Prefer cast_if_present.

@cor3ntin
Copy link
Contributor

Can you provide a better description for the PR (such as "[clang] fix a crash with block captures inside a lambda" ) and an entry in clang/docs/ReleaseNotes.rst referencing the fixed issue?

Thanks!

The change itself looks good except modulo @zyn0217's feedback

@ahatanak ahatanak changed the title Use cast_or_null instead of cast [clang] Fix a crash when a variable is captured by a block nested inside a lambda May 30, 2024
Copy link
Contributor

@cor3ntin cor3ntin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for the fix!

Copy link
Contributor

@mizvekov mizvekov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see it stops crashing, but is it actually working now?

Why hasInit returns true, but the variable doesn't actually have an initializer?
Shouldn't the fix go there?

@ahatanak
Copy link
Collaborator Author

hasInit checks whether VarDecl::Init's pointer is null. But VarDecl::ensureEvaluatedStmt creates an EvaluatedStmt if there isn't one already, after which hasInit returns true.

Eval->Value = Init.get<Stmt *>();

Init.get<Stmt *>() returns null when there's no initializer.

@mizvekov
Copy link
Contributor

hasInit checks whether VarDecl::Init's pointer is null. But VarDecl::ensureEvaluatedStmt creates an
Init.get<Stmt *>() returns null when there's no initializer.

I see. It sounds like it would be less confusing if hasInit would check that as well, or if we need the current behavior elsewhere, we should rename it.

@ahatanak
Copy link
Collaborator Author

I can see if we can make hasInit check that too or rename the function after committing this patch.

Copy link
Contributor

@mizvekov mizvekov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, we can finish in post.

@ahatanak ahatanak merged commit e1c3e16 into llvm:main May 30, 2024
8 checks passed
@ahatanak ahatanak deleted the getinit-check-null branch May 30, 2024 23:52
ahatanaka pushed a commit to swiftlang/llvm-project that referenced this pull request May 31, 2024
…ide a lambda (llvm#93749)

`Eval->Value.get` returns a null pointer when the variable doesn't have
an initializer. Use `cast_if_present` instead of `cast`.

This fixes llvm#93625.

rdar://128482541
(cherry picked from commit e1c3e16)

Conflicts:
	clang/docs/ReleaseNotes.rst
ahatanaka pushed a commit to swiftlang/llvm-project that referenced this pull request May 31, 2024
…ide a lambda (llvm#93749)

`Eval->Value.get` returns a null pointer when the variable doesn't have
an initializer. Use `cast_if_present` instead of `cast`.

This fixes llvm#93625.

rdar://128482541
(cherry picked from commit e1c3e16)

Conflicts:
	clang/docs/ReleaseNotes.rst
ahatanak added a commit to ahatanak/llvm-project that referenced this pull request Jun 5, 2024
VarDecl::isNull() doesn't tell whether the VarDecl has an initializer as
methods like ensureEvaluatedStmt can create an EvaluatedStmt even when
there isn't an initializer.

Revert e1c3e16 as the change isn't
needed anymore with this change.

See the discussion in llvm#93749.
ahatanak added a commit that referenced this pull request Jun 14, 2024
VarDecl::isNull() doesn't tell whether the VarDecl has an initializer as
methods like ensureEvaluatedStmt can create an EvaluatedStmt even when
there isn't an initializer.

Revert e1c3e16 as the change isn't
needed anymore with this change.

See the discussion in #93749.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

clang crashes with c++2b when variable is captured by a block nested inside a lambda
6 participants