diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -45,6 +45,24 @@ -xc++-header``) is now ``.pch`` instead of ``.gch``. - ``-include a.h`` probing ``a.h.gch`` is deprecated. Change the extension name to ``.pch`` or use ``-include-pch a.h.gch``. +- Fixed a bug that caused ``__has_cpp_attribute`` and ``__has_c_attribute`` + return incorrect values for some C++-11-style attributes. Below is a complete + list of behavior changes. + + .. csv-table:: + :header: Test, Old value, New value + + ``__has_cpp_attribute(unused)``, 201603, 0 + ``__has_cpp_attribute(gnu::unused)``, 201603, 1 + ``__has_c_attribute(unused)``, 202106, 0 + ``__has_cpp_attribute(clang::fallthrough)``, 201603, 1 + ``__has_cpp_attribute(gnu::fallthrough)``, 201603, 1 + ``__has_c_attribute(gnu::fallthrough)``, 201910, 1 + ``__has_cpp_attribute(warn_unused_result)``, 201907, 0 + ``__has_cpp_attribute(clang::warn_unused_result)``, 201907, 1 + ``__has_cpp_attribute(gnu::warn_unused_result)``, 201907, 1 + ``__has_c_attribute(warn_unused_result)``, 202003, 0 + ``__has_c_attribute(gnu::warn_unused_result)``, 202003, 1 C++ Specific Potentially Breaking Changes ----------------------------------------- diff --git a/clang/test/Preprocessor/has_attribute.c b/clang/test/Preprocessor/has_attribute.c --- a/clang/test/Preprocessor/has_attribute.c +++ b/clang/test/Preprocessor/has_attribute.c @@ -10,6 +10,11 @@ int __always_inline__(); #endif +// CHECK: warn_unused_result +#if __has_attribute(warn_unused_result) +int warn_unused_result(); +#endif + // CHECK: no_dummy_attribute #if !__has_attribute(dummy_attribute) int no_dummy_attribute(); diff --git a/clang/test/Preprocessor/has_attribute.cpp b/clang/test/Preprocessor/has_attribute.cpp --- a/clang/test/Preprocessor/has_attribute.cpp +++ b/clang/test/Preprocessor/has_attribute.cpp @@ -3,14 +3,14 @@ #define CXX11(x) x: __has_cpp_attribute(x) -// CHECK: clang::fallthrough: 201603L +// CHECK: clang::fallthrough: 1 CXX11(clang::fallthrough) // CHECK: selectany: 0 CXX11(selectany) // The attribute name can be bracketed with double underscores. -// CHECK: clang::__fallthrough__: 201603L +// CHECK: clang::__fallthrough__: 1 CXX11(clang::__fallthrough__) // The scope cannot be bracketed with double underscores unless it is @@ -18,12 +18,21 @@ // CHECK: __gsl__::suppress: 0 CXX11(__gsl__::suppress) -// CHECK: _Clang::fallthrough: 201603L +// CHECK: _Clang::fallthrough: 1 CXX11(_Clang::fallthrough) // CHECK: __nodiscard__: 201907L CXX11(__nodiscard__) +// CHECK: warn_unused_result: 0 +CXX11(warn_unused_result) + +// CHECK: gnu::warn_unused_result: 1 +CXX11(gnu::warn_unused_result) + +// CHECK: clang::warn_unused_result: 1 +CXX11(clang::warn_unused_result) + // CHECK: __gnu__::__const__: 1 CXX11(__gnu__::__const__) diff --git a/clang/test/Preprocessor/has_c_attribute.c b/clang/test/Preprocessor/has_c_attribute.c --- a/clang/test/Preprocessor/has_c_attribute.c +++ b/clang/test/Preprocessor/has_c_attribute.c @@ -9,6 +9,15 @@ // CHECK: __nodiscard__: 202003L C2x(__nodiscard__) +// CHECK: warn_unused_result: 0 +C2x(warn_unused_result) + +// CHECK: gnu::warn_unused_result: 1 +C2x(gnu::warn_unused_result) + +// CHECK: clang::warn_unused_result: 0 +C2x(clang::warn_unused_result) + // CHECK: selectany: 0 C2x(selectany); // Known attribute not supported in C mode @@ -27,10 +36,10 @@ // CHECK: maybe_unused: 202106L C2x(maybe_unused) -// CHECK: __gnu__::warn_unused_result: 202003L +// CHECK: __gnu__::warn_unused_result: 1 C2x(__gnu__::warn_unused_result) -// CHECK: gnu::__warn_unused_result__: 202003L +// CHECK: gnu::__warn_unused_result__: 1 C2x(gnu::__warn_unused_result__) // Test that macro expansion of the builtin argument works. diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -3434,9 +3434,10 @@ } static void GenerateHasAttrSpellingStringSwitch( - const std::vector &Attrs, raw_ostream &OS, - const std::string &Variety = "", const std::string &Scope = "") { - for (const auto *Attr : Attrs) { + const std::vector> &Attrs, + raw_ostream &OS, const std::string &Variety, + const std::string &Scope = "") { + for (const auto &[Attr, Spelling] : Attrs) { // C++11-style attributes have specific version information associated with // them. If the attribute has no scope, the version information must not // have the default value (1), as that's incorrect. Instead, the unscoped @@ -3455,26 +3456,22 @@ // a way that is impactful to the end user. int Version = 1; - std::vector Spellings = GetFlattenedSpellings(*Attr); + assert(Spelling.variety() == Variety); std::string Name = ""; - for (const auto &Spelling : Spellings) { - if (Spelling.variety() == Variety && - (Spelling.nameSpace().empty() || Scope == Spelling.nameSpace())) { - Name = Spelling.name(); - Version = static_cast( - Spelling.getSpellingRecord().getValueAsInt("Version")); - // Verify that explicitly specified CXX11 and C23 spellings (i.e. - // not inferred from Clang/GCC spellings) have a version that's - // different than the default (1). - bool RequiresValidVersion = - (Variety == "CXX11" || Variety == "C23") && - Spelling.getSpellingRecord().getValueAsString("Variety") == Variety; - if (RequiresValidVersion && Scope.empty() && Version == 1) - PrintError(Spelling.getSpellingRecord().getLoc(), - "Standard attributes must have " - "valid version information."); - break; - } + if (Spelling.nameSpace().empty() || Scope == Spelling.nameSpace()) { + Name = Spelling.name(); + Version = static_cast( + Spelling.getSpellingRecord().getValueAsInt("Version")); + // Verify that explicitly specified CXX11 and C23 spellings (i.e. + // not inferred from Clang/GCC spellings) have a version that's + // different from the default (1). + bool RequiresValidVersion = + (Variety == "CXX11" || Variety == "C23") && + Spelling.getSpellingRecord().getValueAsString("Variety") == Variety; + if (RequiresValidVersion && Scope.empty() && Version == 1) + PrintError(Spelling.getSpellingRecord().getLoc(), + "Standard attributes must have " + "valid version information."); } std::string Test; @@ -3514,10 +3511,8 @@ std::string TestStr = !Test.empty() ? Test + " ? " + llvm::itostr(Version) + " : 0" : llvm::itostr(Version); - for (const auto &S : Spellings) - if (Variety.empty() || (Variety == S.variety() && - (Scope.empty() || Scope == S.nameSpace()))) - OS << " .Case(\"" << S.name() << "\", " << TestStr << ")\n"; + if (Scope.empty() || Scope == Spelling.nameSpace()) + OS << " .Case(\"" << Spelling.name() << "\", " << TestStr << ")\n"; } OS << " .Default(0);\n"; } @@ -3550,8 +3545,11 @@ // Separate all of the attributes out into four group: generic, C++11, GNU, // and declspecs. Then generate a big switch statement for each of them. std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); - std::vector Declspec, Microsoft, GNU, Pragma, HLSLSemantic; - std::map> CXX, C23; + std::vector> Declspec, Microsoft, + GNU, Pragma, HLSLSemantic; + std::map>> + CXX, C23; // Walk over the list of all attributes, and split them out based on the // spelling variety. @@ -3560,19 +3558,19 @@ for (const auto &SI : Spellings) { const std::string &Variety = SI.variety(); if (Variety == "GNU") - GNU.push_back(R); + GNU.emplace_back(R, SI); else if (Variety == "Declspec") - Declspec.push_back(R); + Declspec.emplace_back(R, SI); else if (Variety == "Microsoft") - Microsoft.push_back(R); + Microsoft.emplace_back(R, SI); else if (Variety == "CXX11") - CXX[SI.nameSpace()].push_back(R); + CXX[SI.nameSpace()].emplace_back(R, SI); else if (Variety == "C23") - C23[SI.nameSpace()].push_back(R); + C23[SI.nameSpace()].emplace_back(R, SI); else if (Variety == "Pragma") - Pragma.push_back(R); + Pragma.emplace_back(R, SI); else if (Variety == "HLSLSemantic") - HLSLSemantic.push_back(R); + HLSLSemantic.emplace_back(R, SI); } } @@ -3594,7 +3592,10 @@ OS << " return llvm::StringSwitch(Name)\n"; GenerateHasAttrSpellingStringSwitch(HLSLSemantic, OS, "HLSLSemantic"); auto fn = [&OS](const char *Spelling, - const std::map> &List) { + const std::map< + std::string, + std::vector>> + &List) { OS << "case AttributeCommonInfo::Syntax::AS_" << Spelling << ": {\n"; // C++11-style attributes are further split out based on the Scope. for (auto I = List.cbegin(), E = List.cend(); I != E; ++I) {