llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Akash Manna (akash-manna-sky)

<details>
<summary>Changes</summary>

Fixes #<!-- -->214128

When an undeclared name is followed by `&lt;`, the parser tentatively looks for 
a template-argument list and, on invalid input, can skip all the way to the end 
of the file. Every token after that point is lexed and cached while the 
type-trait keywords still have their keyword kind. The first `struct 
__make_unsigned` then reverts the keyword to an identifier, but the second 
`struct __make_unsigned` is replayed from the cache with the stale keyword 
kind, so `TryKeywordIdentFallback` tries to revert the same `IdentifierInfo` 
again and trips the assertion in `revertTokenIDToIdentifier`. The same thing 
can happen with any keyword this hack covers (`__is_pod`, `__is_signed`, the 
transform traits) and with any cached token, since a `Token` is just a snapshot 
of the classification at lex time.

`TryKeywordIdentFallback` now asks the `IdentifierInfo`, which is the source of 
truth, before doing anything: if the keyword has already been made an 
identifier for the remainder of the translation unit, the token kind is simply 
fixed up and no diagnostic or revert happens a second time. First encounters 
behave exactly as before.

LLM tools were used for this contribution. I've reviewed, built, and tested the 
change myself before pushing to GitHub.


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


3 Files Affected:

- (modified) clang/docs/ReleaseNotes.md (+5) 
- (modified) clang/lib/Parse/Parser.cpp (+10-1) 
- (added) clang/test/Parser/GH214128.cpp (+20) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index bdbabf2cd98d0..4056e01f559aa 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -551,6 +551,11 @@ features cannot lower the translation-unit ABI level;
   inside a union caused the union to be treated as a polymorphic class.
   (#GH213854)
 
+- Fixed an assertion when a type-trait keyword that had already been made
+  available as an identifier (e.g. `struct __make_unsigned`) was seen again
+  in a token that was lexed and cached before the first occurrence was parsed.
+  (#GH214128)
+
 #### Bug Fixes to AST Handling
 
 - Fixed a non-deterministic ordering of unused local typedefs that made
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index bad81ea92cd2d..84059a41238cc 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1849,11 +1849,20 @@ SourceLocation Parser::getEndOfPreviousToken() const {
 
 bool Parser::TryKeywordIdentFallback(bool DisableKeyword) {
   assert(Tok.isNot(tok::identifier));
+  IdentifierInfo *II = Tok.getIdentifierInfo();
+
+  // A token lexed and cached before an earlier fallback reverted this keyword
+  // still carries the stale keyword kind; it is already an identifier.
+  if (II->getTokenID() == tok::identifier) {
+    Tok.setKind(tok::identifier);
+    return true;
+  }
+
   Diag(Tok, diag::ext_keyword_as_ident)
     << PP.getSpelling(Tok)
     << DisableKeyword;
   if (DisableKeyword)
-    Tok.getIdentifierInfo()->revertTokenIDToIdentifier();
+    II->revertTokenIDToIdentifier();
   Tok.setKind(tok::identifier);
   return true;
 }
diff --git a/clang/test/Parser/GH214128.cpp b/clang/test/Parser/GH214128.cpp
new file mode 100644
index 0000000000000..f112c33e0234d
--- /dev/null
+++ b/clang/test/Parser/GH214128.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,garbage -DGARBAGE %s
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// The undeclared template-name 'foo' starts a tentative parse that caches the
+// rest of the file, so the second '__make_unsigned' token keeps its keyword
+// kind after the first one has already been reverted to an identifier.
+#ifdef GARBAGE
+foo < bar()
+// garbage-error@-1 {{no template named 'foo'}}
+// garbage-error@-2 {{use of undeclared identifier 'bar'}}
+// garbage-error@-3 {{expected '>'}}
+// garbage-note@-4 {{to match this '<'}}
+#endif
+
+namespace N { // garbage-error {{expected unqualified-id}}
+template <typename _Tp> struct __make_unsigned { typedef _Tp __type; };
+// expected-warning@-1 {{keyword '__make_unsigned' will be made available as 
an identifier for the remainder of the translation unit}}
+struct __make_unsigned<char> { typedef char __type; };
+// expected-error@-1 {{template specialization requires 'template<>'}}
+} // namespace N

``````````

</details>


https://github.com/llvm/llvm-project/pull/219799
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to