[clang] [clang][CUDA/HIP] Fix parsing of `operator<<<...>` (PR #218384)

via cfe-commits cfe-commits at lists.llvm.org
Mon Aug 24 05:04:51 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Mariya Podchishchaeva (Fznamznon)

<details>
<summary>Changes</summary>

In CUDA mode Lexer merges `<<<` into a single token and Parser fails to recognize that it is actually a template specialization of `operator<<` and not a CUDA kernel call expression. Split `<<<` following operator token to `<<` and `<`.

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


3 Files Affected:

- (modified) clang/include/clang/Lex/Lexer.h (+1-1) 
- (modified) clang/lib/Parse/ParseExprCXX.cpp (+24) 
- (modified) clang/test/Parser/cuda-kernel-call-c++11.cu (+17) 


``````````diff
diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h
index b042e5fb088fa..f96d4d72feb1a 100644
--- a/clang/include/clang/Lex/Lexer.h
+++ b/clang/include/clang/Lex/Lexer.h
@@ -395,7 +395,7 @@ class Lexer : public PreprocessorLexer {
                                             const LangOptions &LangOpts);
 
   /// Get the physical length (including trigraphs and escaped newlines) of the
-  /// first \p Characters characters of the token starting at TokStart.
+  /// first \p CharNo characters of the token starting at TokStart.
   static unsigned getTokenPrefixLength(SourceLocation TokStart,
                                        unsigned CharNo,
                                        const SourceManager &SM,
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index f9a0dcc7d53af..883da4907b8eb 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -2485,6 +2485,30 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
       Actions.CodeCompletion().CodeCompleteOperatorName(getCurScope());
       return true;
     }
+    case tok::lesslessless: {
+      // For CUDA, the Lexer will greedily merge all three <<< in operator<<<
+      // which, in fact, can be a valid template specialization of operator<<,
+      // and will never be a valid kernel launch expression, so split.
+      bool CachingTokens = PP.IsPreviousCachedToken(Tok);
+      // If there was a cache, we should update it when doing token split.
+      // The code below never does.
+      assert(!CachingTokens && "No cache expected");
+
+      SourceLocation TokLoc = Tok.getLocation();
+      unsigned LessLessLength = Lexer::getTokenPrefixLength(
+          TokLoc, /*CharNo=*/2, PP.getSourceManager(), getLangOpts());
+
+      SourceLocation LessLoc = PP.SplitToken(TokLoc, LessLessLength);
+      unsigned OldLength = Tok.getLength();
+
+      Tok.setKind(tok::less);
+      Tok.setLength(OldLength - LessLessLength);
+      Tok.setLocation(LessLoc);
+
+      SymbolLocations[SymbolIdx++] = TokLoc;
+      Op = OO_LessLess;
+      break;
+     }
 
     default:
       break;
diff --git a/clang/test/Parser/cuda-kernel-call-c++11.cu b/clang/test/Parser/cuda-kernel-call-c++11.cu
index ef71e2a9acf45..f2fd8ab06ce88 100644
--- a/clang/test/Parser/cuda-kernel-call-c++11.cu
+++ b/clang/test/Parser/cuda-kernel-call-c++11.cu
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify -x hip %s
 
 template<typename T=int> struct S {};
 template<typename> void f();
@@ -33,3 +34,19 @@ template<typename ...T>
 void bar(T... args) {
   S<S<V<void(T)...>>> s7;
 }
+
+template <typename T, typename T1> void operator<<(T, T1);
+
+struct S1 {};
+
+template <> void operator<<<>(S1, S1);
+
+class C {
+public:
+  template <typename T> void operator<<(T) {}
+};
+
+void foobar() {
+  C CC;
+  CC.operator<<<int>(1);
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/218384


More information about the cfe-commits mailing list