Skip to content

[clang][AST] Invalidate DecompositionDecl if it has invalid initializer. #72428

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

Merged
merged 4 commits into from
Jan 17, 2024

Conversation

hokein
Copy link
Collaborator

@hokein hokein commented Nov 15, 2023

Fix #67495, #72198

We build ill-formed AST nodes for invalid structured binding. For case int [_, b] = {0, 0};, the DecompositionDecl is valid, and its children BindingDecls are valid but with a NULL type, this breaks clang invariants in many places, and using these BindingDecls can lead to crashes. This patch fixes them by marking the DecompositionDecl and its children invalid.

CC @ADKaster, @ecnelises

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Nov 15, 2023
@llvmbot
Copy link
Member

llvmbot commented Nov 15, 2023

@llvm/pr-subscribers-clang

Author: Haojian Wu (hokein)

Changes

Fix #67495, #72198

CC @ADKaster, @ecnelises


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

2 Files Affected:

  • (modified) clang/lib/Sema/SemaDecl.cpp (+9)
  • (modified) clang/test/AST/ast-dump-invalid-initialized.cpp (+14-1)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 3876eb501083acb..b89031425e4b5ac 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -13540,6 +13540,15 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) {
           CreateRecoveryExpr(Init->getBeginLoc(), Init->getEndLoc(), Args);
       if (RecoveryExpr.get())
         VDecl->setInit(RecoveryExpr.get());
+      // In general, for error recovery purposes, the initalizer doesn't play
+      // part in the valid bit of the declaration. There are a few exceptions:
+      //  1) if the var decl has a deduced auto type, and the type cannot be
+      //     deduced by an invalid initializer;
+      //  2) if the var decl is decompsition decl with a concrete type (e.g.
+      //    `int [a, b] = 1;`), and the initializer is invalid;
+      // Case 1) is already handled earlier in this function.
+      if (llvm::isa<DecompositionDecl>(VDecl)) // Case 2)
+        VDecl->setInvalidDecl();
       return;
     }
 
diff --git a/clang/test/AST/ast-dump-invalid-initialized.cpp b/clang/test/AST/ast-dump-invalid-initialized.cpp
index 1c374ae716a9db5..a71a02f0f60039e 100644
--- a/clang/test/AST/ast-dump-invalid-initialized.cpp
+++ b/clang/test/AST/ast-dump-invalid-initialized.cpp
@@ -24,4 +24,17 @@ void test() {
   auto b4 = A(1);
   // CHECK: `-VarDecl {{.*}} invalid b5 'auto'
   auto b5 = A{1};
-}
\ No newline at end of file
+}
+
+void pr72198() {
+  // CHECK: DecompositionDecl {{.*}} invalid 'int'
+  int [_, b] = {0, 0};
+  [b]{};
+}
+
+int get_point();
+void pr67495() {
+  // CHECK: DecompositionDecl {{.*}} invalid 'int &'
+  auto& [x, y] = get_point();
+  [x, y] {};
+}

@hnrklssn
Copy link
Member

What differentiates the DecompositionDecl such that we need to mark the decl invalid when there's an error in the RHS, while for other decls we don't? It seems inconsistent.

// `int [a, b] = 1;`), and the initializer is invalid;
// Case 1) is already handled earlier in this function.
if (llvm::isa<DecompositionDecl>(VDecl)) // Case 2)
VDecl->setInvalidDecl();
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should move the 1) part of the comment line 13496, or just remove it entirely.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We should have the comment in both places. This is very helpful to understanding the code.

I agree that having the comment far away from the code that it is commenting on is less useful. The code could easily be moved in a refactor or in other ways and become completely disconnected.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think this comment is helpful (especially the context is not obvious here). I'd like to keep it.

The code handling the case 1) is https://github.com/llvm/llvm-project/blob/1c05fe350064aa3a1784bb09829a07d501842d97/clang/lib/Sema/SemaDecl.cpp#L13363C3-L13384, and it is easier to understand the purpose by reading the code. I'd avoid having two similar comments in the codebase.

@hokein hokein force-pushed the invalidated-decomposition-decl branch from 13b97a2 to 067a93a Compare November 17, 2023 10:07
@hokein
Copy link
Collaborator Author

hokein commented Nov 17, 2023

What differentiates the DecompositionDecl such that we need to mark the decl invalid when there's an error in the RHS, while for other decls we don't? It seems inconsistent.

DecompositionDecl (aka structure binding) is special here, a legal DecompositionDecl must be declared with a deduced auto type, so the its initializer should play part of the decl's invalid bit. For the error case where a DecompositionDecl with a non-deduced type, clang still builds the Decl AST node (with invalid bit) for better recovery. There are some oversight cases. This patch is fixing those.

@hnrklssn
Copy link
Member

What differentiates the DecompositionDecl such that we need to mark the decl invalid when there's an error in the RHS, while for other decls we don't? It seems inconsistent.

DecompositionDecl (aka structure binding) is special here, a legal DecompositionDecl must be declared with a deduced auto type, so the its initializer should play part of the decl's invalid bit. For the error case where a DecompositionDecl with a non-deduced type, clang still builds the Decl AST node (with invalid bit) for better recovery. There are some oversight cases. This patch is fixing those.

Ah that makes sense then. I didn't realise it had to be auto.

@hokein
Copy link
Collaborator Author

hokein commented Dec 18, 2023

Friendly ping.

Copy link
Collaborator

@shafik shafik left a comment

Choose a reason for hiding this comment

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

We need a release note and please add a more detailed summary. A description of the problem being solved and the solution to the fix provides.

@hokein hokein force-pushed the invalidated-decomposition-decl branch from 067a93a to ff9d909 Compare December 19, 2023 09:17
@hokein
Copy link
Collaborator Author

hokein commented Dec 19, 2023

We need a release note and please add a more detailed summary. A description of the problem being solved and the solution to the fix provides.

Done.

@hokein hokein force-pushed the invalidated-decomposition-decl branch from ff9d909 to 7ecf5d7 Compare December 19, 2023 14:03
@hokein hokein force-pushed the invalidated-decomposition-decl branch from 7ecf5d7 to 9eb93e9 Compare January 8, 2024 09:36
@hokein
Copy link
Collaborator Author

hokein commented Jan 8, 2024

Friendly ping.

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

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

LGTM with a tiny nit.

@hokein hokein force-pushed the invalidated-decomposition-decl branch from 9eb93e9 to a0ad535 Compare January 17, 2024 08:30
@hokein hokein merged commit 8f7fdd9 into llvm:main Jan 17, 2024
@hokein hokein deleted the invalidated-decomposition-decl branch January 17, 2024 11:11
ampandey-1995 pushed a commit to ampandey-1995/llvm-project that referenced this pull request Jan 19, 2024
…er. (llvm#72428)

Fix llvm#67495, llvm#72198

We build ill-formed AST nodes for invalid structured binding. For case
`int [_, b] = {0, 0};`, the `DecompositionDecl` is valid, and its
children `BindingDecl`s are valid but with a NULL type, this breaks
clang invariants in many places, and using these `BindingDecl`s can lead
to crashes. This patch fixes them by marking the DecompositionDecl and
its children invalid.
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: Crash on capturing invalid structured binding declaration in lambda expression (regression from 44f2baa3804a62)
6 participants