Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -142,6 +142,22 @@ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Implemented `DR2397 `_ which allows ``auto`` specifier for pointers and reference to arrays. +- Implemented `CWG2521 `_ which reserves using ``__`` in user defined literal suffixes + for C++ implementations, and deprecates literal operator function declarations using an identifier. + Taught ``-Wuser-defined-literals`` for the former, on by default for C++23 language mode, + and added ``-Wdeprecated-literal-operator`` for the latter, off by default. + + .. code-block:: c++ + + // What follows is warned by -Wuser-defined-literals + // albeit "ill-formed, no diagnostic required". + // Its behavior is undefined, [reserved.names.general]p2. + string operator ""__i18n(const char*, std::size_t); + + // Let's assume this is not in the global namespace. + // -Wdeprecated-literal-operator whines about extra spaces. + string operator "" _i18n(const char*, std::size_t); + // ^ an extra space C Language Changes ------------------ Index: clang/include/clang/Basic/DiagnosticGroups.td =================================================================== --- clang/include/clang/Basic/DiagnosticGroups.td +++ clang/include/clang/Basic/DiagnosticGroups.td @@ -183,6 +183,7 @@ def DeprecatedCopyWithUserProvidedDtor : DiagGroup<"deprecated-copy-with-user-provided-dtor">; def DeprecatedCopy : DiagGroup<"deprecated-copy", [DeprecatedCopyWithUserProvidedCopy]>; def DeprecatedCopyWithDtor : DiagGroup<"deprecated-copy-with-dtor", [DeprecatedCopyWithUserProvidedDtor]>; +def DeprecatedLiteralOperator : DiagGroup<"deprecated-literal-operator">; // For compatibility with GCC. def : DiagGroup<"deprecated-copy-dtor", [DeprecatedCopyWithDtor]>; def DeprecatedDeclarations : DiagGroup<"deprecated-declarations">; @@ -219,6 +220,7 @@ DeprecatedEnumFloatConversion, DeprecatedBuiltins, DeprecatedIncrementBool, + DeprecatedLiteralOperator, DeprecatedPragma, DeprecatedRegister, DeprecatedThisCapture, @@ -862,7 +864,7 @@ def ReservedModuleIdentifier : DiagGroup<"reserved-module-identifier">; def ReservedIdentifier : DiagGroup<"reserved-identifier", - [ReservedIdAsMacro, ReservedModuleIdentifier]>; + [ReservedIdAsMacro, ReservedModuleIdentifier, UserDefinedLiterals]>; // Unreachable code warning groups. // Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -406,6 +406,9 @@ "it starts with '_' followed by a capital letter|" "it contains '__'}1">, InGroup, DefaultIgnore; +def warn_deprecated_literal_operator_id: Warning< + "identifier '%0' preceded by space(s) in the literal operator declaration " + "is deprecated">, InGroup; def warn_reserved_module_name : Warning< "%0 is a reserved name for a module">, InGroup; @@ -9261,8 +9264,8 @@ "string literal operator templates are a GNU extension">, InGroup; def warn_user_literal_reserved : Warning< - "user-defined literal suffixes not starting with '_' are reserved" - "%select{; no literal will invoke this operator|}0">, + "user-defined literal suffixes %select{|not starting with '_'|containing '__'}0 are reserved" + "%select{; no literal will invoke this operator|}1">, InGroup; // C++ conversion functions Index: clang/include/clang/Basic/IdentifierTable.h =================================================================== --- clang/include/clang/Basic/IdentifierTable.h +++ clang/include/clang/Basic/IdentifierTable.h @@ -50,6 +50,12 @@ ContainsDoubleUnderscore, }; +enum class ReservedLiteralSuffixIdStatus { + NotReserved = 0, + NotStartsWithUnderscore, + ContainsDoubleUnderscore, +}; + /// Determine whether an identifier is reserved for use as a name at global /// scope. Such identifiers might be implementation-specific global functions /// or variables. @@ -491,6 +497,10 @@ /// 7.1.3, C++ [lib.global.names]). ReservedIdentifierStatus isReserved(const LangOptions &LangOpts) const; + /// Determine whether \p this is a name reserved for future standardization or + /// the implementation (C++ [usrlit.suffix]). + ReservedLiteralSuffixIdStatus isReservedLiteralSuffixId() const; + /// If the identifier is an "uglified" reserved name, return a cleaned form. /// e.g. _Foo => Foo. Otherwise, just returns the name. StringRef deuglifiedName() const; Index: clang/lib/Basic/IdentifierTable.cpp =================================================================== --- clang/lib/Basic/IdentifierTable.cpp +++ clang/lib/Basic/IdentifierTable.cpp @@ -397,6 +397,19 @@ return ReservedIdentifierStatus::NotReserved; } +ReservedLiteralSuffixIdStatus +IdentifierInfo::isReservedLiteralSuffixId() const { + StringRef Name = getName(); + + if (Name[0] != '_') + return ReservedLiteralSuffixIdStatus::NotStartsWithUnderscore; + + if (Name.contains("__")) + return ReservedLiteralSuffixIdStatus::ContainsDoubleUnderscore; + + return ReservedLiteralSuffixIdStatus::NotReserved; +} + StringRef IdentifierInfo::deuglifiedName() const { StringRef Name = getName(); if (Name.size() >= 2 && Name.front() == '_' && Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -16447,15 +16447,18 @@ } } - StringRef LiteralName - = FnDecl->getDeclName().getCXXLiteralIdentifier()->getName(); - if (LiteralName[0] != '_' && + auto *II = FnDecl->getDeclName().getCXXLiteralIdentifier(); + auto Status = II->isReservedLiteralSuffixId(); + if (Status != ReservedLiteralSuffixIdStatus::NotReserved && !getSourceManager().isInSystemHeader(FnDecl->getLocation())) { - // C++11 [usrlit.suffix]p1: - // Literal suffix identifiers that do not start with an underscore - // are reserved for future standardization. + // C++23 [usrlit.suffix]p1: + // Literal suffix identifiers that do not start with an underscore are + // reserved for future standardization. Literal suffix identifiers that + // contain a double underscore __ are reserved for use by C++ + // implementations. Diag(FnDecl->getLocation(), diag::warn_user_literal_reserved) - << StringLiteralParser::isValidUDSuffix(getLangOpts(), LiteralName); + << static_cast(Status) + << StringLiteralParser::isValidUDSuffix(getLangOpts(), II->getName()); } return false; Index: clang/lib/Sema/SemaExprCXX.cpp =================================================================== --- clang/lib/Sema/SemaExprCXX.cpp +++ clang/lib/Sema/SemaExprCXX.cpp @@ -502,13 +502,17 @@ IdentifierInfo *II = Name.Identifier; ReservedIdentifierStatus Status = II->isReserved(PP.getLangOpts()); SourceLocation Loc = Name.getEndLoc(); - if (isReservedInAllContexts(Status) && - !PP.getSourceManager().isInSystemHeader(Loc)) { - Diag(Loc, diag::warn_reserved_extern_symbol) - << II << static_cast(Status) - << FixItHint::CreateReplacement( - Name.getSourceRange(), - (StringRef("operator\"\"") + II->getName()).str()); + if (!PP.getSourceManager().isInSystemHeader(Loc)) { + if (auto Hint = FixItHint::CreateReplacement( + Name.getSourceRange(), + (StringRef("operator\"\"") + II->getName()).str()); + isReservedInAllContexts(Status)) { + Diag(Loc, diag::warn_reserved_extern_symbol) + << II << static_cast(Status) << Hint; + } else if (LangOpts.CPlusPlus23) { + Diag(Loc, diag::warn_deprecated_literal_operator_id) + << II->getName() << Hint; + } } } Index: clang/test/CXX/drs/dr25xx.cpp =================================================================== --- clang/test/CXX/drs/dr25xx.cpp +++ clang/test/CXX/drs/dr25xx.cpp @@ -1,4 +1,14 @@ -// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify +// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors + +#if __cplusplus < 201103L +// expected-no-diagnostics +#endif namespace dr2516 { // dr2516: yes // NB: reusing 1482 test @@ -13,9 +23,13 @@ namespace dr2518 { // dr2518: 17 +#if __cplusplus >= 201103L template void f(T t) { if constexpr (sizeof(T) != sizeof(int)) { +#if __cplusplus < 201703L +// expected-error@-2 {{constexpr if is a C++17 extension}} +#endif static_assert(false, "must be int-sized"); // expected-error {{must be int-size}} } } @@ -28,6 +42,9 @@ template struct S { static_assert(false); // expected-error {{static assertion failed}} +#if __cplusplus < 201703L +// expected-error@-2 {{'static_assert' with no message is a C++17 extension}} +#endif }; template <> @@ -41,11 +58,28 @@ S s2; S s3; // expected-note {{in instantiation of template class 'dr2518::S' requested here}} } +#endif } +namespace dr2521 { // dr2521: 17 +#if __cplusplus >= 202302L +long double operator"" _\u03C0___(long double); +// expected-warning@-1 {{identifier '_π___' preceded by space(s) in the literal operator declaration is deprecated}} +// expected-warning@-2 {{user-defined literal suffixes containing '__' are reserved}} + +template decltype(sizeof 0) +operator"" _div(); +// expected-warning@-1 {{identifier '_div' preceded by space(s) in the literal operator declaration is deprecated}} + +using ::dr2521::operator"" _\u03C0___; +using ::dr2521::operator""_div; +// expected-warning@-2 {{identifier '_π___' preceded by space(s) in the literal operator declaration is deprecated}} +#endif +} // namespace dr2521 namespace dr2565 { // dr2565: 16 open +#if __cplusplus >= 202002L template concept C = requires (typename T::type x) { x + 1; @@ -107,4 +141,5 @@ // expected-error@-1{{static assertion failed}} // expected-note@-2{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} +#endif } Index: clang/www/cxx_dr_status.html =================================================================== --- clang/www/cxx_dr_status.html +++ clang/www/cxx_dr_status.html @@ -14933,7 +14933,7 @@ 2521 DR User-defined literals and reserved identifiers - Unknown + Clang 17 2522