https://github.com/Fznamznon created https://github.com/llvm/llvm-project/pull/218384
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 `<`. >From 233982bacdb13ade41716feae01536d8de88c5a3 Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva <[email protected]> Date: Fri, 21 Aug 2026 11:56:47 -0500 Subject: [PATCH] [clang][CUDA/HIP] Fix parsing of `operator<<<...>` In CUDA mode Lexer merges <<< into a single token and Parser fails to recognize that it is actually a template specialization of operator<<. Split <<< following operator token to << and <. --- clang/include/clang/Lex/Lexer.h | 2 +- clang/lib/Parse/ParseExprCXX.cpp | 24 +++++++++++++++++++++ clang/test/Parser/cuda-kernel-call-c++11.cu | 17 +++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) 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); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
