Skip to content

[clang][Sema] Fix crash on decomposition decl missing initializer - #210151

Merged
patrykstefanski merged 1 commit into
llvm:mainfrom
patrykstefanski:fix-decomp-null-token-deref
Jul 17, 2026
Merged

patrykstefanski merged 1 commit into
llvm:mainfrom
patrykstefanski:fix-decomp-null-token-deref

Conversation

@patrykstefanski

Copy link
Copy Markdown
Member

ActOnUninitializedDecl dereferenced the std::optional from Lexer::findNextToken() unconditionally when diagnosing a structured binding with no initializer. Guard the optional and fall back to the declaration's location.

@patrykstefanski patrykstefanski added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jul 16, 2026
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-clang

Author: Patryk Stefanski (patrykstefanski)

Changes

ActOnUninitializedDecl dereferenced the std::optional<Token> from Lexer::findNextToken() unconditionally when diagnosing a structured binding with no initializer. Guard the optional and fall back to the declaration's location.


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

2 Files Affected:

  • (modified) clang/lib/Sema/SemaDecl.cpp (+10-6)
  • (modified) clang/test/Parser/cxx1z-decomposition.cpp (+3)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index c5920f03ed6e1..b7679ece0bfb5 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14521,12 +14521,16 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
     }
     // C++1z [dcl.dcl]p1 grammar implies that an initializer is mandatory.
     if (isa<DecompositionDecl>(RealDecl)) {
-      // Point the caret to the token immediately after the closing bracket.
-      auto NextLoc = dyn_cast<DecompositionDecl>(RealDecl)->getRSquareLoc();
-      NextLoc =
-          Lexer::findNextToken(NextLoc, PP.getSourceManager(), PP.getLangOpts())
-              ->getLocation();
-      Diag(NextLoc, diag::err_decomp_decl_requires_init) << Var;
+      // Point the caret to the token immediately after the closing bracket if
+      // it can be found; otherwise fall back to the declaration's location.
+      SourceLocation Loc = Var->getLocation();
+      SourceLocation RSquareLoc =
+          dyn_cast<DecompositionDecl>(RealDecl)->getRSquareLoc();
+      if (std::optional<Token> Next = Lexer::findNextToken(
+              RSquareLoc, PP.getSourceManager(), PP.getLangOpts());
+          Next.has_value())
+        Loc = Next->getLocation();
+      Diag(Loc, diag::err_decomp_decl_requires_init) << Var;
       Var->setInvalidDecl();
       return;
     }
diff --git a/clang/test/Parser/cxx1z-decomposition.cpp b/clang/test/Parser/cxx1z-decomposition.cpp
index fb22364ddb802..607a628506e8a 100644
--- a/clang/test/Parser/cxx1z-decomposition.cpp
+++ b/clang/test/Parser/cxx1z-decomposition.cpp
@@ -152,6 +152,7 @@ namespace Template {
 }
 
 #define MYC C
+#define CLOSE_NO_INIT ] ;
 
 namespace Init {
   template<typename T> T f(T t) {
@@ -171,6 +172,8 @@ namespace Init {
     T t1 = t; // check that uninitialized structured binding declaration error works with templates and macros
     auto [t0, t2] MYC = {t, t1}; // expected-error{{structured binding declaration '[t0, t2]' requires an initializer; expected '=' or braced initializer list}} expected-error{{expected ';' at end of declaration}}
                                  // CHECK: :[[@LINE-1]]:19: error: structured binding declaration '[t0, t2]' requires an initializer; expected '=' or braced initializer list
+    auto [bad4 CLOSE_NO_INIT // expected-error {{structured binding declaration '[bad4]' requires an initializer; expected '=' or braced initializer list}}
+                             // CHECK: :[[@LINE-1]]:10: error: structured binding declaration '[bad4]' requires an initializer; expected '=' or braced initializer list
   }
 }
 

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

🐧 Linux x64 Test Results

  • 90423 tests passed
  • 1690 tests skipped
  • 1 test failed

Failed Tests

(click on a test name to see its output)

cross-project-tests

cross-project-tests.intrinsic-header-tests/riscv_packed_simd.c
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 4
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/clang /home/gha/actions-runner/_work/llvm-project/llvm-project/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c -O2 -S -o - --target=riscv32    -menable-experimental-extensions -march=rv32i_p0p21    -Werror -Wextra -Xclang -verify    | /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck /home/gha/actions-runner/_work/llvm-project/llvm-project/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c --check-prefixes=CHECK,RV32
# executed command: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/clang /home/gha/actions-runner/_work/llvm-project/llvm-project/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c -O2 -S -o - --target=riscv32 -menable-experimental-extensions -march=rv32i_p0p21 -Werror -Wextra -Xclang -verify
# note: command had no output on stdout or stderr
# executed command: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck /home/gha/actions-runner/_work/llvm-project/llvm-project/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c --check-prefixes=CHECK,RV32
# .---command stderr------------
# | /home/gha/actions-runner/_work/llvm-project/llvm-project/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c:2372:10: error: RV32: expected string not found in input
# | // RV32: pncvt.b
# |          ^
# | <stdin>:3712:19: note: scanning from here
# | test_punzipe_i8x4: # @test_punzipe_i8x4
# |                   ^
# | <stdin>:3713:13: note: possible intended match here
# | # %bb.0: # %entry
# |             ^
# | /home/gha/actions-runner/_work/llvm-project/llvm-project/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c:2377:10: error: RV32: expected string not found in input
# | // RV32: pncvth.b
# |          ^
# | <stdin>:3725:19: note: scanning from here
# | test_punzipo_i8x4: # @test_punzipo_i8x4
# |                   ^
# | <stdin>:3736:12: note: possible intended match here
# |  # -- End function
# |            ^
# | /home/gha/actions-runner/_work/llvm-project/llvm-project/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c:2382:10: error: RV32: expected string not found in input
# | // RV32: pncvt.b
# |          ^
# | <stdin>:3740:19: note: scanning from here
# | test_punzipe_u8x4: # @test_punzipe_u8x4
# |                   ^
# | <stdin>:3741:13: note: possible intended match here
# | # %bb.0: # %entry
# |             ^
# | /home/gha/actions-runner/_work/llvm-project/llvm-project/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c:2387:10: error: RV32: expected string not found in input
# | // RV32: pncvth.b
# |          ^
# | <stdin>:3753:19: note: scanning from here
# | test_punzipo_u8x4: # @test_punzipo_u8x4
# |                   ^
# | <stdin>:3764:12: note: possible intended match here
# |  # -- End function
# |            ^
# | /home/gha/actions-runner/_work/llvm-project/llvm-project/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c:2392:10: error: RV32: expected string not found in input
# | // RV32: pncvt.h
# |          ^
# | <stdin>:3768:20: note: scanning from here
# | test_punzipe_i16x2: # @test_punzipe_i16x2
# |                    ^
# | <stdin>:3769:13: note: possible intended match here
# | # %bb.0: # %entry
# |             ^
# | /home/gha/actions-runner/_work/llvm-project/llvm-project/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c:2397:10: error: RV32: expected string not found in input
# | // RV32: pncvth.h
# |          ^
# | <stdin>:3778:20: note: scanning from here
# | test_punzipo_i16x2: # @test_punzipo_i16x2
# |                    ^
# | <stdin>:3786:12: note: possible intended match here
# |  # -- End function
# |            ^
# | /home/gha/actions-runner/_work/llvm-project/llvm-project/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c:2402:10: error: RV32: expected string not found in input
# | // RV32: pncvt.h
# |          ^
# | <stdin>:3790:20: note: scanning from here
# | test_punzipe_u16x2: # @test_punzipe_u16x2
# |                    ^
# | <stdin>:3791:13: note: possible intended match here
# | # %bb.0: # %entry
# |             ^
# | /home/gha/actions-runner/_work/llvm-project/llvm-project/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c:2407:10: error: RV32: expected string not found in input
# | // RV32: pncvth.h
# |          ^
# | <stdin>:3800:20: note: scanning from here
# | test_punzipo_u16x2: # @test_punzipo_u16x2
# |                    ^
# | <stdin>:3808:12: note: possible intended match here
# |  # -- End function
# |            ^
# | 
# | Input file: <stdin>
# | Check file: /home/gha/actions-runner/_work/llvm-project/llvm-project/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |               .
# |               .
# |               .
# |            3707:  .size test_pzip_u16x4, .Lfunc_end355-test_pzip_u16x4 
# |            3708:  # -- End function 
# |            3709:  .globl test_punzipe_i8x4 # -- Begin function test_punzipe_i8x4 
# |            3710:  .p2align 2 
# |            3711:  .type test_punzipe_i8x4,@function 
# |            3712: test_punzipe_i8x4: # @test_punzipe_i8x4 
# | check:2372'0                      {                        search range start (exclusive)
# | check:2372'1                                               error: no match found in search range
# |            3713: # %bb.0: # %entry 
# | check:2372'2                 ?       possible intended match
# |            3714:  srli a3, a1, 16 
# |            3715:  srli a2, a0, 16 
# |            3716:  ppaire.db a0, a0, a2 
# |            3717:  pack a0, a0, a1 
# |            3718:  ret 
# |            3719: .Lfunc_end356: 
# |            3720:  .size test_punzipe_i8x4, .Lfunc_end356-test_punzipe_i8x4 
# |            3721:  # -- End function 
# |            3722:  .globl test_punzipo_i8x4 # -- Begin function test_punzipo_i8x4 
# |            3723:  .p2align 2 
# |            3724:  .type test_punzipo_i8x4,@function 
# |            3725: test_punzipo_i8x4: # @test_punzipo_i8x4 
# | check:2372'3                       }                       search range end (exclusive)
# | check:2377'0                      {                        search range start (exclusive)
# | check:2377'1                                               error: no match found in search range
# |            3726: # %bb.0: # %entry 
# |            3727:  srli a3, a1, 24 
# |            3728:  srli a1, a1, 8 
# |            3729:  srli a2, a0, 24 
# |            3730:  srli a0, a0, 8 
# |            3731:  ppaire.db a0, a0, a2 
# |            3732:  pack a0, a0, a1 
# |            3733:  ret 
# |            3734: .Lfunc_end357: 
# |            3735:  .size test_punzipo_i8x4, .Lfunc_end357-test_punzipo_i8x4 
# |            3736:  # -- End function 
# | check:2377'2                ?         possible intended match
# |            3737:  .globl test_punzipe_u8x4 # -- Begin function test_punzipe_u8x4 
# |            3738:  .p2align 2 
# |            3739:  .type test_punzipe_u8x4,@function 
# |            3740: test_punzipe_u8x4: # @test_punzipe_u8x4 
# | check:2377'3                       }                       search range end (exclusive)
# | check:2382'0                      {                        search range start (exclusive)
# | check:2382'1                                               error: no match found in search range
# |            3741: # %bb.0: # %entry 
# | check:2382'2                 ?       possible intended match
# |            3742:  srli a3, a1, 16 
# |            3743:  srli a2, a0, 16 
# |            3744:  ppaire.db a0, a0, a2 
# |            3745:  pack a0, a0, a1 
# |            3746:  ret 
# |            3747: .Lfunc_end358: 
# |            3748:  .size test_punzipe_u8x4, .Lfunc_end358-test_punzipe_u8x4 
# |            3749:  # -- End function 
# |            3750:  .globl test_punzipo_u8x4 # -- Begin function test_punzipo_u8x4 
# |            3751:  .p2align 2 
# |            3752:  .type test_punzipo_u8x4,@function 
# |            3753: test_punzipo_u8x4: # @test_punzipo_u8x4 
# | check:2382'3                       }                       search range end (exclusive)
# | check:2387'0                      {                        search range start (exclusive)
# | check:2387'1                                               error: no match found in search range
# |            3754: # %bb.0: # %entry 
# |            3755:  srli a3, a1, 24 
# |            3756:  srli a1, a1, 8 
# |            3757:  srli a2, a0, 24 
# |            3758:  srli a0, a0, 8 
# |            3759:  ppaire.db a0, a0, a2 
# |            3760:  pack a0, a0, a1 
# |            3761:  ret 
# |            3762: .Lfunc_end359: 
# |            3763:  .size test_punzipo_u8x4, .Lfunc_end359-test_punzipo_u8x4 
# |            3764:  # -- End function 
# | check:2387'2                ?         possible intended match
# |            3765:  .globl test_punzipe_i16x2 # -- Begin function test_punzipe_i16x2 
# |            3766:  .p2align 2 
# |            3767:  .type test_punzipe_i16x2,@function 
# |            3768: test_punzipe_i16x2: # @test_punzipe_i16x2 
# | check:2387'3                        }                        search range end (exclusive)
# | check:2392'0                       {                         search range start (exclusive)
# | check:2392'1                                                 error: no match found in search range
# |            3769: # %bb.0: # %entry 
# | check:2392'2                 ?       possible intended match
# |            3770:  pack a0, a0, a1 
# |            3771:  ret 
# |            3772: .Lfunc_end360: 
# |            3773:  .size test_punzipe_i16x2, .Lfunc_end360-test_punzipe_i16x2 
# |            3774:  # -- End function 
# |            3775:  .globl test_punzipo_i16x2 # -- Begin function test_punzipo_i16x2 
# |            3776:  .p2align 2 
# |            3777:  .type test_punzipo_i16x2,@function 
# |            3778: test_punzipo_i16x2: # @test_punzipo_i16x2 
# | check:2392'3                        }                        search range end (exclusive)
# | check:2397'0                       {                         search range start (exclusive)
# | check:2397'1                                                 error: no match found in search range
# |            3779: # %bb.0: # %entry 
# |            3780:  srli a1, a1, 16 
# |            3781:  srli a0, a0, 16 
# |            3782:  pack a0, a0, a1 
# |            3783:  ret 
# |            3784: .Lfunc_end361: 
# |            3785:  .size test_punzipo_i16x2, .Lfunc_end361-test_punzipo_i16x2 
# |            3786:  # -- End function 
# | check:2397'2                ?         possible intended match
# |            3787:  .globl test_punzipe_u16x2 # -- Begin function test_punzipe_u16x2 
# |            3788:  .p2align 2 
# |            3789:  .type test_punzipe_u16x2,@function 
# |            3790: test_punzipe_u16x2: # @test_punzipe_u16x2 
# | check:2397'3                        }                        search range end (exclusive)
# | check:2402'0                       {                         search range start (exclusive)
# | check:2402'1                                                 error: no match found in search range
# |            3791: # %bb.0: # %entry 
# | check:2402'2                 ?       possible intended match
# |            3792:  pack a0, a0, a1 
# |            3793:  ret 
# |            3794: .Lfunc_end362: 
# |            3795:  .size test_punzipe_u16x2, .Lfunc_end362-test_punzipe_u16x2 
# |            3796:  # -- End function 
# |            3797:  .globl test_punzipo_u16x2 # -- Begin function test_punzipo_u16x2 
# |            3798:  .p2align 2 
# |            3799:  .type test_punzipo_u16x2,@function 
# |            3800: test_punzipo_u16x2: # @test_punzipo_u16x2 
# | check:2402'3                        }                        search range end (exclusive)
# | check:2407'0                       {                         search range start (exclusive)
# | check:2407'1                                                 error: no match found in search range
# |            3
# | ...
# `---data was truncated (10240/11140) (change limit with -D output_limit=N)
# error: command failed with exit status: 1

--

If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the infrastructure label.

@erichkeane erichkeane left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 suggestion, else I think this is sensible to me.

SourceLocation Loc = Var->getLocation();
SourceLocation RSquareLoc =
dyn_cast<DecompositionDecl>(RealDecl)->getRSquareLoc();
if (std::optional<Token> Next = Lexer::findNextToken(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

optional has an operator bool, right? Why are you not just testing it?

Ala:

if (std::optional<Token> Next = Lexer::findNextToken(RSquareLoc, PP.getSourceManager(), PP.getLangOpts()) ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks for the feedback, updated to your suggestion.

@patrykstefanski
patrykstefanski force-pushed the fix-decomp-null-token-deref branch from a28f981 to f874288 Compare July 16, 2026 23:22

@erichkeane erichkeane left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just needs a release note, else this LGTM.

@patrykstefanski

Copy link
Copy Markdown
Member Author

Just needs a release note, else this LGTM.

22ca91c (PR #127924) introduced the crash and landed a week ago on the dev branch. The new diagnostic didn't ship yet. Should we still add 'crash fix' to release notes or rather skip it?

@erichkeane

Copy link
Copy Markdown
Contributor

Just needs a release note, else this LGTM.

22ca91c (PR #127924) introduced the crash and landed a week ago on the dev branch. The new diagnostic didn't ship yet. Should we still add 'crash fix' to release notes or rather skip it?

A "week ago" is still old enough to have made it into the 23.0 branch! So we still need a release note HERE, but we probably also want to cherry-pick this to the release branch.

Also, see you have a formatter error that you have to fix, linux test seems unrelated.

@patrykstefanski
patrykstefanski force-pushed the fix-decomp-null-token-deref branch from f874288 to 868542c Compare July 17, 2026 16:57
@AaronBallman

Copy link
Copy Markdown
Contributor

Just needs a release note, else this LGTM.

22ca91c (PR #127924) introduced the crash and landed a week ago on the dev branch. The new diagnostic didn't ship yet. Should we still add 'crash fix' to release notes or rather skip it?

A "week ago" is still old enough to have made it into the 23.0 branch! So we still need a release note HERE, but we probably also want to cherry-pick this to the release branch.

If we're cherry-picking to the release branch, no need for the release note at all, right? Clang 22.x is fine today and Clang 23.x will be fine after the cherry-pick, so Clang 24.x (main branch) doesn't need a note either?

@erichkeane

Copy link
Copy Markdown
Contributor

Just needs a release note, else this LGTM.

22ca91c (PR #127924) introduced the crash and landed a week ago on the dev branch. The new diagnostic didn't ship yet. Should we still add 'crash fix' to release notes or rather skip it?

A "week ago" is still old enough to have made it into the 23.0 branch! So we still need a release note HERE, but we probably also want to cherry-pick this to the release branch.

If we're cherry-picking to the release branch, no need for the release note at all, right? Clang 22.x is fine today and Clang 23.x will be fine after the cherry-pick, so Clang 24.x (main branch) doesn't need a note either?

Oh, right! For some reason I thought we were already 23.1. Yep, @patrykstefanski : Please remove teh release note now (sorry!), and feel free to merge once CI agrees. After that, you/we can do the cherry-pick.

ActOnUninitializedDecl dereferenced the std::optional<Token> from
Lexer::findNextToken() unconditionally when diagnosing a structured
binding with no initializer. Guard the optional and fall back to the
declaration's location.
@patrykstefanski
patrykstefanski force-pushed the fix-decomp-null-token-deref branch from 868542c to dd698af Compare July 17, 2026 17:04
@patrykstefanski

Copy link
Copy Markdown
Member Author

Unrelated test cross-project-tests.intrinsic-header-tests/riscv_packed_simd.c is failing.

@patrykstefanski
patrykstefanski merged commit 0e3852f into llvm:main Jul 17, 2026
9 of 11 checks passed
@patrykstefanski patrykstefanski added this to the LLVM 23.x Release milestone Jul 17, 2026
@github-project-automation github-project-automation Bot moved this to Needs Triage in LLVM Release Status Jul 17, 2026
@github-project-automation github-project-automation Bot moved this from Needs Triage to Done in LLVM Release Status Jul 17, 2026
@patrykstefanski

Copy link
Copy Markdown
Member Author

/cherry-pick 0e3852f

@llvmbot

llvmbot commented Jul 17, 2026

Copy link
Copy Markdown
Member

/pull-request #210444

dyung pushed a commit to llvmbot/llvm-project that referenced this pull request Jul 22, 2026
…vm#210151)

ActOnUninitializedDecl dereferenced the std::optional<Token> from
Lexer::findNextToken() unconditionally when diagnosing a structured
binding with no initializer. Guard the optional and fall back to the
declaration's location.

(cherry picked from commit 0e3852f)
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

Development

Successfully merging this pull request may close these issues.

4 participants