https://github.com/cor3ntin created 
https://github.com/llvm/llvm-project/pull/212131

As a DR in all language modes.
Because we already supported the feature this is only modifying the extensions 
warnings.

>From 7ee4747282606218f344bbdf38dec5f1d935a946 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <[email protected]>
Date: Sun, 26 Jul 2026 18:43:08 +0200
Subject: [PATCH] [Clang] Implement P3658R1

As a DR in all language modes.
Because we already supported the feature this is only modifying
the extensions warnings.
---
 clang/docs/ReleaseNotes.md                    |  4 +++
 .../include/clang/Basic/DiagnosticLexKinds.td |  2 ++
 clang/lib/Lex/Lexer.cpp                       | 35 +++++++++++--------
 clang/lib/Lex/UnicodeCharSets.h               |  6 ++--
 clang/test/Lexer/unicode.c                    | 20 +++++++----
 clang/www/cxx_status.html                     |  2 +-
 6 files changed, 42 insertions(+), 27 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 83a2b10d96046..7958f11e9cae0 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -91,6 +91,10 @@ features cannot lower the translation-unit ABI level;
 
 #### C++2d Feature Support
 
+- Clang now supports [P3658R1](https://wg21.link/p3658r1) (Adjust identifier
+  following new Unicode recommendations), applied as a DR to all C++ language
+  modes.
+
 #### C++2c Feature Support
 
 - Clang now supports 
[P3533R2](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3533r2.html)
 (constexpr virtual inheritance).
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td 
b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 18143fc9f0feb..8330166e05199 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -145,6 +145,8 @@ def warn_utf8_symbol_homoglyph : Warning<
 def warn_utf8_symbol_zero_width : Warning<
   "identifier contains character %0 that is invisible in "
   "some environments">, InGroup<DiagGroup<"unicode-zero-width">>;
+defm mathematical_notation : CXX29Compat<
+  "mathematical notation character %0 in an identifier is">;
 def ext_mathematical_notation : ExtWarn<
   "mathematical notation character %0 in an identifier is a Clang extension">,
   InGroup<DiagGroup<"mathematical-notation-identifier-extension">>;
diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index 5826485c8b16a..d63865b024b82 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -1603,12 +1603,9 @@ static bool isUnicodeWhitespace(uint32_t Codepoint) {
   return UnicodeWhitespaceChars.contains(Codepoint);
 }
 
-// To mitigate https://github.com/llvm/llvm-project/issues/54732,
-// we allow "Mathematical Notation Characters" in identifiers.
-// This is a proposed profile that extends the XID_Start/XID_continue
-// with mathematical symbols, superscipts and subscripts digits
-// found in some production software.
-// https://www.unicode.org/L2/L2022/22230-math-profile.pdf
+// The mathematical compatibility notation profile extends XID_Start and
+// XID_Continue with mathematical symbols, superscript and subscript digits.
+// https://www.unicode.org/reports/tr31/#Mathematical_Compatibility_Notation
 static bool isMathematicalExtensionID(uint32_t C, const LangOptions &LangOpts,
                                       bool IsStart, bool &IsExtension) {
   static const llvm::sys::UnicodeCharSet MathStartChars(
@@ -1677,8 +1674,10 @@ static bool isAllowedInitiallyIDChar(uint32_t C, const 
LangOptions &LangOpts,
   return !C99DisallowedInitialIDChars.contains(C);
 }
 
-static void diagnoseExtensionInIdentifier(DiagnosticsEngine &Diags, uint32_t C,
-                                          CharSourceRange Range) {
+static void
+diagnoseMathematicalNotationInIdentifier(DiagnosticsEngine &Diags,
+                                         const LangOptions &LangOpts,
+                                         uint32_t C, CharSourceRange Range) {
 
   static const llvm::sys::UnicodeCharSet MathStartChars(
       MathematicalNotationProfileIDStartRanges);
@@ -1689,7 +1688,11 @@ static void 
diagnoseExtensionInIdentifier(DiagnosticsEngine &Diags, uint32_t C,
   (void)MathContinueChars;
   assert((MathStartChars.contains(C) || MathContinueChars.contains(C)) &&
          "Unexpected mathematical notation codepoint");
-  Diags.Report(Range.getBegin(), diag::ext_mathematical_notation)
+  unsigned DiagID = LangOpts.CPlusPlus
+                        ? DiagnosticIDs::getCompatDiagId(
+                              LangOpts, diag_compat::mathematical_notation)
+                        : diag::ext_mathematical_notation;
+  Diags.Report(Range.getBegin(), DiagID)
       << EscapeSingleCodepointForDiagnostic(C) << Range;
 }
 
@@ -1861,8 +1864,9 @@ bool Lexer::tryConsumeIdentifierUCN(const char *&CurPtr, 
unsigned Size,
     // Carry on as if the codepoint was valid for recovery purposes.
   } else if (!isLexingRawMode()) {
     if (IsExtension)
-      diagnoseExtensionInIdentifier(PP->getDiagnostics(), CodePoint,
-                                    makeCharRange(*this, CurPtr, UCNPtr));
+      diagnoseMathematicalNotationInIdentifier(
+          PP->getDiagnostics(), LangOpts, CodePoint,
+          makeCharRange(*this, CurPtr, UCNPtr));
 
     maybeDiagnoseIDCharCompat(PP->getDiagnostics(), CodePoint,
                               makeCharRange(*this, CurPtr, UCNPtr),
@@ -1917,8 +1921,8 @@ bool Lexer::tryConsumeIdentifierUTF8Char(const char 
*&CurPtr, Token &Result) {
     // valid for recovery purposes.
   } else if (!isLexingRawMode()) {
     if (IsExtension)
-      diagnoseExtensionInIdentifier(
-          PP->getDiagnostics(), CodePoint,
+      diagnoseMathematicalNotationInIdentifier(
+          PP->getDiagnostics(), LangOpts, CodePoint,
           makeCharRange(*this, CharStart, UnicodePtr));
     maybeDiagnoseIDCharCompat(PP->getDiagnostics(), CodePoint,
                               makeCharRange(*this, CharStart, UnicodePtr),
@@ -1942,8 +1946,9 @@ bool Lexer::LexUnicodeIdentifierStart(Token &Result, 
uint32_t C,
     if (!isLexingRawMode() && !ParsingPreprocessorDirective &&
         !PP->isPreprocessedOutput()) {
       if (IsExtension)
-        diagnoseExtensionInIdentifier(PP->getDiagnostics(), C,
-                                      makeCharRange(*this, BufferPtr, CurPtr));
+        diagnoseMathematicalNotationInIdentifier(
+            PP->getDiagnostics(), LangOpts, C,
+            makeCharRange(*this, BufferPtr, CurPtr));
       maybeDiagnoseIDCharCompat(PP->getDiagnostics(), C,
                                 makeCharRange(*this, BufferPtr, CurPtr),
                                 /*IsFirst=*/true);
diff --git a/clang/lib/Lex/UnicodeCharSets.h b/clang/lib/Lex/UnicodeCharSets.h
index 0ceab85d15586..eaee53172050f 100644
--- a/clang/lib/Lex/UnicodeCharSets.h
+++ b/clang/lib/Lex/UnicodeCharSets.h
@@ -393,9 +393,7 @@ static const llvm::sys::UnicodeCharRange 
XIDContinueRanges[] = {
     {0x1FBF0, 0x1FBF9}, {0xE0100, 0xE01EF},
 };
 
-// Clang supports the "Mathematical notation profile" as an extension,
-// as described in https://www.unicode.org/L2/L2022/22230-math-profile.pdf
-// Math_Start
+// Unicode 18.0 ID_Compat_Math_Start
 static const llvm::sys::UnicodeCharRange
     MathematicalNotationProfileIDStartRanges[] = {
         {0x02202, 0x02202}, // โˆ‚
@@ -413,7 +411,7 @@ static const llvm::sys::UnicodeCharRange
         {0x1D7C3, 0x1D7C3}, // ๐Ÿƒ
 };
 
-// Math_Continue
+// Unicode 18.0 ID_Compat_Math_Continue, excluding ID_Compat_Math_Start
 static const llvm::sys::UnicodeCharRange
     MathematicalNotationProfileIDContinueRanges[] = {
         {0x000B2, 0x000B3}, // ยฒ-ยณ
diff --git a/clang/test/Lexer/unicode.c b/clang/test/Lexer/unicode.c
index 0974c976aee1c..53acca6ba04b7 100644
--- a/clang/test/Lexer/unicode.c
+++ b/clang/test/Lexer/unicode.c
@@ -1,6 +1,8 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -x c -std=c11 %s
-// RUN: %clang_cc1 -fsyntax-only -verify=expected,c2x -x c -std=c2x %s
-// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx -x c++ -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,c2x,math -x c -std=c2x %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx,math,ext -x c++ 
-std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx -x c++ -std=c++2d %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx,math,compat -x c++ 
-std=c++2d -Wpre-c++2d-compat %s
 // RUN: %clang_cc1 -std=c99 -E -DPP_ONLY=1 %s | FileCheck %s 
--strict-whitespace
 // RUN: %clang_cc1 -E -DPP_ONLY=1 %s | FileCheck %s --strict-whitespace
 // UNSUPPORTED: system-zos
@@ -52,19 +54,23 @@ extern int a\N{TANGSALETTERGA}; // expected-error 
{{'TANGSALETTERGA' is not a va
                                 // expected-error {{expected ';' after top 
level declarator}} \
                                 // expected-note {{character names in Unicode 
escape sequences are sensitive to case and whitespace}}
 
-extern int ๐››; // expected-warning {{mathematical notation character '๐››' 
U+1D6DB in an identifier is a Clang extension}}
+// Mathematical notation characters are standard in C++ (P3658R1) and a Clang
+// extension in C. The 'math' prefix matches whichever warning applies.
+extern int ๐››; // c2x-warning {{mathematical notation character '๐››' U+1D6DB in 
an identifier is a Clang extension}} \
+                 ext-warning {{mathematical notation character '๐››' U+1D6DB in 
an identifier is a C++2d extension}} \
+                 compat-warning {{mathematical notation character '๐››' U+1D6DB 
in an identifier is incompatible with C++ standards before C++2d}}
 extern int โ‚‰; // expected-error {{character 'โ‚‰' U+2089 not allowed at the 
start of an identifier}} \\
                  expected-warning {{declaration does not declare anything}}
 
 extern int a\N{PICKLE}; // expected-error {{character '๐Ÿซ' U+1FADD not allowed 
in an identifier}}
 
-int aยนbโ‚โ‚„โ‚‚โ‚Žโˆ‡; // expected-warning 6{{mathematical notation character}}
+int aยนbโ‚โ‚„โ‚‚โ‚Žโˆ‡; // math-warning 6{{mathematical notation character}}
 
-int \u{221E} = 1; // expected-warning {{mathematical notation character}}
+int \u{221E} = 1; // math-warning {{mathematical notation character}}
 int \N{MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL} = 1;
-                 // expected-warning@-1 {{mathematical notation character}}
+                 // math-warning@-1 {{mathematical notation character}}
 
-int a\N{SUBSCRIPT EQUALS SIGN} = 1; // expected-warning {{mathematical 
notation character}}
+int a\N{SUBSCRIPT EQUALS SIGN} = 1; // math-warning {{mathematical notation 
character}}
 
 // This character doesn't have the XID_Start property
 extern int  \U00016AC0; // TANGSA DIGIT ZERO  // cxx-error {{expected 
unqualified-id}} \
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index a12115f8b785a..16ebeb1052b79 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -137,7 +137,7 @@ <h2 id="cxx29">C++2d implementation status</h2>
 <tr>
   <td>Adjust identifier following new Unicode recommendations</td>
   <td><a href="https://wg21.link/P3658R1";>P3658R1</a> <a 
href="#dr">(DR)</a></td>
-  <td class="none" align="center">No</td>
+  <td class="full" align="center">Clang 24</td>
 </tr>
 <tr>
   <td><code>return_value</code> &amp; <code>return_void</code> are not 
mutually exclusive</td>

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

Reply via email to