[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