https://github.com/Fznamznon updated https://github.com/llvm/llvm-project/pull/218384
>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 1/7] [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); +} >From e8e0c20f18b1ebae88006aa4daedd6e0a76913f0 Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva <[email protected]> Date: Tue, 25 Aug 2026 05:32:37 -0500 Subject: [PATCH 2/7] Fix source location, fix cache, add test --- clang/lib/Parse/ParseExprCXX.cpp | 22 +++++++++++++-------- clang/test/Parser/cuda-kernel-call-c++11.cu | 20 +++++++++++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 883da4907b8eb..a25d814d326c4 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -2489,26 +2489,32 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext, // 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); + SourceLocation LessLessLoc = PP.SplitToken(TokLoc, LessLessLength); + Token LessLess = Tok; + LessLess.setLocation(LessLessLoc); + LessLess.setKind(tok::lessless); + LessLess.setLength(LessLessLength); + unsigned OldLength = Tok.getLength(); Tok.setKind(tok::less); Tok.setLength(OldLength - LessLessLength); - Tok.setLocation(LessLoc); + Tok.setLocation(TokLoc.getLocWithOffset(LessLessLength)); - SymbolLocations[SymbolIdx++] = TokLoc; + // Update the cache if there is any. + bool CachingTokens = PP.IsPreviousCachedToken(Tok); + if (CachingTokens) + PP.ReplacePreviousCachedToken({LessLess}); + + SymbolLocations[SymbolIdx++] = LessLessLoc; 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 f2fd8ab06ce88..9223be4e1812a 100644 --- a/clang/test/Parser/cuda-kernel-call-c++11.cu +++ b/clang/test/Parser/cuda-kernel-call-c++11.cu @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s // RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify -x hip %s +// RUN: not %clang_cc1 %s -DSLOC_CHECK 2>&1 | FileCheck %s --strict-whitespace template<typename T=int> struct S {}; template<typename> void f(); @@ -49,4 +50,23 @@ public: void foobar() { C CC; CC.operator<<<int>(1); + CC.template operator<<<int>(1); +#ifdef SLOC_CHECK + // We split <<< into a << followed by a <, check that < has right source + // location. + CC.operator<<<int; + // CHECK: error: expected '>' + // CHECK-NEXT: CC.operator<<<int; + // CHECK-NEXT: ^ + // CHECK-NEXT: to match this '<' + // CHECK-NEXT: CC.operator<<<int; + // CHECK-NEXT: ^ + CC.template operator<<<int; + // CHECK: error: expected '>' + // CHECK-NEXT: CC.template operator<<<int; + // CHECK-NEXT: ^ + // CHECK-NEXT: to match this '<' + // CHECK-NEXT: CC.template operator<<<int; + // CHECK-NEXT: ^ +#endif } >From ad58f4031f0b0be0d88f413ab53bda6c37a7bbff Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva <[email protected]> Date: Tue, 25 Aug 2026 06:13:28 -0500 Subject: [PATCH 3/7] Fix bad test --- clang/test/Parser/cuda-kernel-call-c++11.cu | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clang/test/Parser/cuda-kernel-call-c++11.cu b/clang/test/Parser/cuda-kernel-call-c++11.cu index 9223be4e1812a..4ea91d8477e40 100644 --- a/clang/test/Parser/cuda-kernel-call-c++11.cu +++ b/clang/test/Parser/cuda-kernel-call-c++11.cu @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s // RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify -x hip %s -// RUN: not %clang_cc1 %s -DSLOC_CHECK 2>&1 | FileCheck %s --strict-whitespace +// RUN: not %clang_cc1 -fsyntax-only %s -DSLOC_CHECK 2>&1 | FileCheck %s --strict-whitespace template<typename T=int> struct S {}; template<typename> void f(); @@ -55,18 +55,18 @@ void foobar() { // We split <<< into a << followed by a <, check that < has right source // location. CC.operator<<<int; - // CHECK: error: expected '>' + // CHECK: [[@LINE-1]]:20: error: expected '>' // CHECK-NEXT: CC.operator<<<int; // CHECK-NEXT: ^ - // CHECK-NEXT: to match this '<' + // CHECK-NEXT: [[@LINE-4]]:16: note: to match this '<' // CHECK-NEXT: CC.operator<<<int; // CHECK-NEXT: ^ CC.template operator<<<int; - // CHECK: error: expected '>' + // CHECK: [[@LINE-1]]:29: error: expected '>' // CHECK-NEXT: CC.template operator<<<int; // CHECK-NEXT: ^ - // CHECK-NEXT: to match this '<' + // CHECK-NEXT: [[@LINE-4]]:25: note: to match this '<' // CHECK-NEXT: CC.template operator<<<int; - // CHECK-NEXT: ^ + // CHECK-NEXT: ^ #endif } >From 4d80f7436780077368bcae253153bc02e92f0062 Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva <[email protected]> Date: Tue, 8 Sep 2026 08:21:17 -0500 Subject: [PATCH 4/7] Fix cache again --- clang/lib/Parse/ParseExprCXX.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index a25d814d326c4..59e49e7d8696a 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -2502,14 +2502,14 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext, unsigned OldLength = Tok.getLength(); + bool CachingTokens = PP.IsPreviousCachedToken(Tok); Tok.setKind(tok::less); Tok.setLength(OldLength - LessLessLength); Tok.setLocation(TokLoc.getLocWithOffset(LessLessLength)); // Update the cache if there is any. - bool CachingTokens = PP.IsPreviousCachedToken(Tok); if (CachingTokens) - PP.ReplacePreviousCachedToken({LessLess}); + PP.ReplacePreviousCachedToken({LessLess, Tok}); SymbolLocations[SymbolIdx++] = LessLessLoc; Op = OO_LessLess; >From 4be85e7d690a0655dbdb3d515bbb9ddf7fd0af48 Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva <[email protected]> Date: Tue, 8 Sep 2026 08:21:40 -0500 Subject: [PATCH 5/7] Fix tentative parsing too --- clang/lib/Parse/ParseTentative.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 07d45925e892e..c71ce09267f8a 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -858,6 +858,13 @@ Parser::TPResult Parser::TryParseOperatorId() { } break; + case tok::lesslessless: + // In CUDA/HIP mode the lexer merges <<< into a single token. Inside + // operator<<<T> this can only be operator<< followed by a template-arg <, + // so treat it as a valid operator-function-id during tentative parsing. + ConsumeToken(); + return TPResult::True; + default: break; } >From df41100a7fc512b7668b69bd9ee67ddf0dd62f53 Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva <[email protected]> Date: Tue, 8 Sep 2026 08:22:02 -0500 Subject: [PATCH 6/7] Move the test to a separate file --- clang/test/Parser/cuda-kernel-call-c++11.cu | 37 ------------- .../test/Parser/cuda-operator-lesslessless.cu | 53 +++++++++++++++++++ 2 files changed, 53 insertions(+), 37 deletions(-) create mode 100644 clang/test/Parser/cuda-operator-lesslessless.cu diff --git a/clang/test/Parser/cuda-kernel-call-c++11.cu b/clang/test/Parser/cuda-kernel-call-c++11.cu index 4ea91d8477e40..ef71e2a9acf45 100644 --- a/clang/test/Parser/cuda-kernel-call-c++11.cu +++ b/clang/test/Parser/cuda-kernel-call-c++11.cu @@ -1,6 +1,4 @@ // RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s -// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify -x hip %s -// RUN: not %clang_cc1 -fsyntax-only %s -DSLOC_CHECK 2>&1 | FileCheck %s --strict-whitespace template<typename T=int> struct S {}; template<typename> void f(); @@ -35,38 +33,3 @@ 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); - CC.template operator<<<int>(1); -#ifdef SLOC_CHECK - // We split <<< into a << followed by a <, check that < has right source - // location. - CC.operator<<<int; - // CHECK: [[@LINE-1]]:20: error: expected '>' - // CHECK-NEXT: CC.operator<<<int; - // CHECK-NEXT: ^ - // CHECK-NEXT: [[@LINE-4]]:16: note: to match this '<' - // CHECK-NEXT: CC.operator<<<int; - // CHECK-NEXT: ^ - CC.template operator<<<int; - // CHECK: [[@LINE-1]]:29: error: expected '>' - // CHECK-NEXT: CC.template operator<<<int; - // CHECK-NEXT: ^ - // CHECK-NEXT: [[@LINE-4]]:25: note: to match this '<' - // CHECK-NEXT: CC.template operator<<<int; - // CHECK-NEXT: ^ -#endif -} diff --git a/clang/test/Parser/cuda-operator-lesslessless.cu b/clang/test/Parser/cuda-operator-lesslessless.cu new file mode 100644 index 0000000000000..0c884910be6d8 --- /dev/null +++ b/clang/test/Parser/cuda-operator-lesslessless.cu @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify -x hip %s +// RUN: not %clang_cc1 -fsyntax-only %s -DSLOC_CHECK 2>&1 | FileCheck %s + +// Make sure operator followed by <<< is parsed as << and < since int CUDA/HIP +// it can never be a kernel launch expression. + +template <typename T, typename T1> void operator<<(T, T1); // expected-error {{overloaded 'operator<<' must have at least one parameter of class or enumeration type}} \ + // expected-note {{candidate template ignored: substitution failure [with T = int, T1 = int]}} + +struct S1 {}; + +template <> void operator<<<>(S1, S1); + +class C { +public: + template <typename T> void operator<<(T) {} +}; + +void foobar() { + C CC; + CC.operator<<<int>(1); + CC.template operator<<<int>(1); +#ifdef SLOC_CHECK + // In CUDA/HIP mode <<< is a single token that gets split into << and <. + // Verify that the < retains the correct source location after the split. + CC.operator<<<int; + // CHECK: [[@LINE-1]]:20: error: expected '>' + // CHECK-NEXT: CC.operator<<<int; + // CHECK-NEXT: ^ + // CHECK-NEXT: [[@LINE-4]]:16: note: to match this '<' + // CHECK-NEXT: CC.operator<<<int; + // CHECK-NEXT: ^ + CC.template operator<<<int; + // CHECK: [[@LINE-1]]:29: error: expected '>' + // CHECK-NEXT: CC.template operator<<<int; + // CHECK-NEXT: ^ + // CHECK-NEXT: [[@LINE-4]]:25: note: to match this '<' + // CHECK-NEXT: CC.template operator<<<int; + // CHECK-NEXT: ^ +#endif +} + +// Verify TryParseOperatorId handles tok::lesslessless as well, so the invalid +// code below produces clearer errors. +template<typename T> int operator<<(int, T) { return 0; } // expected-error {{overloaded 'operator<<' must have at least one parameter of class or enumeration type}} \ + // expected-note {{candidate template ignored: substitution failure [with T = int]}} + +void test() { + int(operator<<<int>(1, 2)); // expected-error {{no matching function for call to 'operator<<'}} \ + // expected-note {{in instantiation of function template specialization 'operator<<<int>' requested here}} \ + // expected-note {{in instantiation of function template specialization 'operator<<<int, int>' requested here}} +} >From 1f5a188968a50dbe29df56e43319b42631cfd9c3 Mon Sep 17 00:00:00 2001 From: Mariya Podchishchaeva <[email protected]> Date: Tue, 8 Sep 2026 08:29:11 -0500 Subject: [PATCH 7/7] Fix comments --- clang/test/Parser/cuda-operator-lesslessless.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Parser/cuda-operator-lesslessless.cu b/clang/test/Parser/cuda-operator-lesslessless.cu index 0c884910be6d8..f745f724a8782 100644 --- a/clang/test/Parser/cuda-operator-lesslessless.cu +++ b/clang/test/Parser/cuda-operator-lesslessless.cu @@ -2,7 +2,7 @@ // RUN: %clang_cc1 -fsyntax-only -verify -x hip %s // RUN: not %clang_cc1 -fsyntax-only %s -DSLOC_CHECK 2>&1 | FileCheck %s -// Make sure operator followed by <<< is parsed as << and < since int CUDA/HIP +// Make sure operator followed by <<< is parsed as << and < since in CUDA/HIP // it can never be a kernel launch expression. template <typename T, typename T1> void operator<<(T, T1); // expected-error {{overloaded 'operator<<' must have at least one parameter of class or enumeration type}} \ _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
