unlock unlimited code runs for $79, limited time →
NOSSLETTER.techLIVE
back to OSSpedia
compiler-internalserror-handlingtesting-strategy

[clang][Parse] Stop parsing declarator chunks after a parenthesized structured binding

Project: llvm/llvm-project | Language: C++ | Concepts: compiler-internals, error-handling, testing-strategy | Difficulty: senior
Link: https://github.com/llvm/llvm-project/pull/219270

The problem

Clang would crash or assert when parsing a parenthesized structured binding followed by a function declarator and body, such as ([a, b])() {}. This is invalid C++, but the parser failed to reject it early, leading to an internal assertion or segmentation fault.

Why it's non-trivial

A naive fix might only check for unparenthesized structured bindings, but the bug occurs specifically when the binding is parenthesized (e.g., ([a, b])). In this case, the parser's logic for stopping after a structured binding was bypassed, so it incorrectly tried to parse further declarator chunks (like ()) and treated the construct as a function, leading to a crash.

The approach

The fix adds an explicit check after parsing a parenthesized declarator: if it turns out to be a structured binding, the parser stops immediately, preventing any further suffixes from being parsed.

Key code snippet:

@@ -6862,6 +6862,11 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
     // Example: 'char (*X)'   or 'int (*XX)(void)'
     ParseParenDeclarator(D);
 
+    // As above, a structured binding declarator cannot be followed by any
+    // declarator chunks.
+    if (D.isDecompositionDeclarator())
+      return;
+
     // If the declarator was parenthesized, we entered the declarator
     // scope when parsing the parenthesized declarator, then exited
     // the scope already. Re-enter the scope, if we need to.
  • ParseParenDeclarator(D); parses the parenthesized declarator.
  • The new check if (D.isDecompositionDeclarator()) return; detects if the result is a structured binding and exits early, preventing the parser from interpreting trailing () as a function declarator.

Test coverage:

A new test file, clang/test/Parser/GH218144.cpp, is added to verify that various invalid structured binding forms are rejected with the correct diagnostics and do not crash the compiler:

+auto ([a])() {}
+// expected-error@-1 {{structured binding declaration cannot be declared with parentheses}}
+// expected-error@-2 {{expected expression}}
+// expected-error@-3 {{expected ';' after top level declarator}}

This ensures that both parenthesized and nested parenthesized structured bindings with trailing function declarators are handled safely.

Release notes:

+- Fixed an assertion failure when a parenthesized structured binding declarator
+  was followed by a function declarator and body (e.g. ``([a, b])() {}``).
+  (#GH218144, #GH193687)

From the review thread

Fznamznon: Can we give the PR title that explains what the patch does in a short way instead of naming the assert? See https://llvm.org/docs/DeveloperPolicy.html#commit-messages .

akash-manna-sky: I changed the PR title...

Takeaways

  • When fixing parser bugs, ensure that all syntactic forms (including parenthesized and nested variants) are covered by the stopping conditions.
  • Defensive parsing: If an invalid construct is detected, halt further parsing immediately to avoid cascading errors or crashes.
  • Add regression tests for each variant of the problematic syntax to prevent future regressions and to ensure correct diagnostics are emitted.