Author: Akash Agrawal Date: 2026-07-28T20:28:27+08:00 New Revision: 8c8cc1cfb9f04fc57716b9ccc3ea74c9a5495bf7
URL: https://github.com/llvm/llvm-project/commit/8c8cc1cfb9f04fc57716b9ccc3ea74c9a5495bf7 DIFF: https://github.com/llvm/llvm-project/commit/8c8cc1cfb9f04fc57716b9ccc3ea74c9a5495bf7.diff LOG: [Clang][Parse] Fix assertion when annotating a failed decltype-specifier (#211221) `ParseDeclaratorInternal's` member-pointer special case enters a speculative scope-specifier parse whenever it sees `decltype`, without checking that `( `follows: ``` if (getLangOpts().CPlusPlus && (Tok.is(tok::coloncolon) || Tok.is(tok::kw_decltype) || ...)) { ``` But `decltype-specifier` is always `decltype ( expression ) — decltype` not followed by `(` can never be valid. When that happens, `ParseOptionalCXXScopeSpecifier` still tries to parse it as one, fails, and error recovery skips tokens. The EndLoc returned is then stale by the time `AnnotateExistingDecltypeSpecifier` uses it to annotate, which trips the invariant in `Preprocessor::AnnotatePreviousCachedTokens`: ``` int decltype; // crashed int *decltype = 0; // crashed ``` Fix: only take this path when decltype is followed by (. `(Tok.is(tok::kw_decltype) && NextToken().is(tok::l_paren)) ||` `ParseDecltypeSpecifier` already requires `(` right after `decltype` or bails with `TST_error`, so this check drops no valid parse — `decltype(expr)` (including as a member-pointer scope, e.g.` int decltype(obj)::*p = &S::m;) `is unaffected. Fixes #211207 Co-authored - Claude-Sonnet Added: clang/test/Parser/decltype-crash.cpp Modified: clang/docs/ReleaseNotes.md clang/lib/Parse/ParseDecl.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 63f898a89f10b..c348ddaf23017 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -388,6 +388,10 @@ features cannot lower the translation-unit ABI level; `[](Types... = args...) {}`). Clang now diagnoses the illegal default argument instead of asserting. (#GH210714) +- Fixed a crash on invalid code where a ``decltype`` not followed by ``(`` was + parsed where a nested-name-specifier could appear (e.g. ``int decltype = 0;``). + Clang now diagnoses the error instead of asserting. (#GH211207) + - Fixed a crash when computing the implicit deletion of a defaulted comparison operator required an access check that ran while an enclosing declaration was still being parsed. (#GH210692) diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index cb2f6c07a6824..9fa3b96527c08 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -6496,8 +6496,12 @@ void Parser::ParseDeclaratorInternal(Declarator &D, // C++ member pointers start with a '::' or a nested-name. // Member pointers get special handling, since there's no place for the // scope spec in the generic path below. + // A 'decltype' can only begin a nested-name-specifier if it is followed by + // '('; otherwise it is not a decltype-specifier at all and must not be + // parsed as one. if (getLangOpts().CPlusPlus && - (Tok.is(tok::coloncolon) || Tok.is(tok::kw_decltype) || + (Tok.is(tok::coloncolon) || + (Tok.is(tok::kw_decltype) && NextToken().is(tok::l_paren)) || (Tok.is(tok::identifier) && (NextToken().is(tok::coloncolon) || NextToken().is(tok::less))) || Tok.is(tok::annot_cxxscope))) { diff --git a/clang/test/Parser/decltype-crash.cpp b/clang/test/Parser/decltype-crash.cpp new file mode 100644 index 0000000000000..af4622df5e54a --- /dev/null +++ b/clang/test/Parser/decltype-crash.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s + +// A 'decltype' that is not followed by '(' fails to parse as a +// decltype-specifier. ParseOptionalCXXScopeSpecifier used to still annotate it +// and treat it as a nested-name-specifier, which tripped an assertion in +// Preprocessor::AnnotatePreviousCachedTokens. It should just diagnose the +// error instead. +int decltype = 0; +// expected-error@-1 {{expected '(' after 'decltype'}} +// expected-error@-2 {{expected unqualified-id}} + +int *decltype = 0; +// expected-error@-1 {{expected '(' after 'decltype'}} +// expected-error@-2 {{expected unqualified-id}} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
