diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -69,8 +69,9 @@ enum AvailabilityResult { AR_Available = 0, AR_NotYetIntroduced, + AR_Extension, AR_Deprecated, - AR_Unavailable + AR_Unavailable, }; /// Decl - This represents one declaration (or definition), e.g. a variable, diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -979,6 +979,14 @@ let Documentation = [AvailabilityDocs]; } +def LibraryExtension : InheritableAttr { + let Spellings = [Clang<"library_extension", /*allowInC=*/0>]; + let Args = [StringArgument<"kind">]; + let Subjects = SubjectList<[Named]>; + let Documentation = [ExtensionDocs]; + let MeaningfulToClassTemplateDefinition = 1; +} + def ExternalSourceSymbol : InheritableAttr { let Spellings = [Clang<"external_source_symbol", /*allowInC=*/1, /*version=*/20230206>]; @@ -4198,4 +4206,3 @@ let Subjects = SubjectList<[TypedefName], ErrorDiag>; let Documentation = [Undocumented]; } - diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1727,6 +1727,30 @@ }]; } +def ExtensionDocs : Documentation { + let Category = DocCatType; + let Content = [{ +The ``clang::extension`` attribute tells the compiler that the annotated symbol +is an extension. This is useful for standard library implementations to inform +users that a given symbol is from a later standard or completely non-standard. + +.. code-block:: c++ + + namespace std { + template > + class [[clang::extension("C++17")]] basic_string_view { /*...*/ }; + + using string_view = basic_string_view; + } + + // Clang produces the warning "std::string_view is a C++17 extension" + void func(std::string_view); + +The currently supported argument arguments are "C++11", "C++14", "C++17", "C++20", +"C++23", "C++26" and "GNU". + }]; +} + def ExternalSourceSymbolDocs : Documentation { let Category = DocCatDecl; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5682,10 +5682,25 @@ InGroup; def note_availability_specified_here : Note< "%0 has been explicitly marked " - "%select{unavailable|deleted|deprecated}1 here">; + "%select{unavailable|deleted|deprecated|as an extension}1 here">; def note_partial_availability_specified_here : Note< "%0 has been marked as being introduced in %1 %2 here, " "but the deployment target is %1 %3">; +def warn_unknown_ext : Warning<"Unknown extension kind: %0">; +def warn_cxx11_ext : Warning<"%0 is a C++11 extension">, + InGroup; +def warn_cxx14_ext : Warning<"%0 is a C++14 extension">, + InGroup; +def warn_cxx17_ext : Warning<"%0 is a C++17 extension">, + InGroup; +def warn_cxx20_ext : Warning<"%0 is a C++20 extension">, + InGroup; +def warn_cxx23_ext : Warning<"%0 is a C++23 extension">, + InGroup; +def warn_cxx26_ext : Warning<"%0 is a C++2c extension">, + InGroup; +def warn_gnu_ext : Warning<"%0 is a GNU extension">, + InGroup; def note_implicitly_deleted : Note< "explicitly defaulted function was implicitly deleted here">; def warn_not_enough_argument : Warning< diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -656,6 +656,22 @@ continue; } + if (const auto* Extension = dyn_cast(A)) { + if (Result >= AR_Extension) + continue; + + auto ExtKind = Extension->getKind(); + if ((!getLangOpts().CPlusPlus26 && ExtKind == "C++26") || + (!getLangOpts().CPlusPlus23 && ExtKind == "C++23") || + (!getLangOpts().CPlusPlus20 && ExtKind == "C++20") || + (!getLangOpts().CPlusPlus17 && ExtKind == "C++17") || + (!getLangOpts().CPlusPlus14 && ExtKind == "C++14") || + (!getLangOpts().CPlusPlus11 && ExtKind == "C++11") || + ExtKind == "GNU") + Result = AR_Extension; + continue; + } + if (const auto *Unavailable = dyn_cast(A)) { if (Message) *Message = std::string(Unavailable->getMessage()); diff --git a/clang/lib/Sema/CodeCompleteConsumer.cpp b/clang/lib/Sema/CodeCompleteConsumer.cpp --- a/clang/lib/Sema/CodeCompleteConsumer.cpp +++ b/clang/lib/Sema/CodeCompleteConsumer.cpp @@ -786,6 +786,7 @@ switch (getDeclAvailability(Declaration)) { case AR_Available: case AR_NotYetIntroduced: + case AR_Extension: Availability = CXAvailability_Available; break; diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp --- a/clang/lib/Sema/SemaAvailability.cpp +++ b/clang/lib/Sema/SemaAvailability.cpp @@ -419,6 +419,31 @@ } return; } + case AR_Extension: { + assert(Message.empty()); + auto ExtKind = OffendingDecl->getAttr()->getKind(); + if (ExtKind == "C++11") + diag = diag::warn_cxx11_ext; + else if (ExtKind == "C++14") + diag = diag::warn_cxx14_ext; + else if (ExtKind == "C++17") + diag = diag::warn_cxx17_ext; + else if (ExtKind == "C++20") + diag = diag::warn_cxx20_ext; + else if (ExtKind == "C++23") + diag = diag::warn_cxx23_ext; + else if (ExtKind == "C++26") + diag = diag::warn_cxx26_ext; + else if (ExtKind == "GNU") + diag = diag::warn_gnu_ext; + + available_here_select_kind = 3; + if (const auto *AL = OffendingDecl->getAttr()) + NoteLocation = AL->getLocation(); + + break; + } + case AR_Deprecated: diag = !ObjCPropertyAccess ? diag::warn_deprecated : diag::warn_property_method_deprecated; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -8220,6 +8220,19 @@ D->addAttr(::new (S.Context) DeprecatedAttr(S.Context, AL, Str, Replacement)); } +static void handleLibraryExtensionAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + StringRef Kind; + if (!S.checkStringLiteralArgumentAttr(AL, 0, Kind)) + return; + if (llvm::none_of(std::array{"C++11", "C++14", "C++17", "C++20", "C++23", + "C++26", "GNU"}, + [&](const char *K) { return K == Kind; })) { + S.Diag(AL.getLoc(), diag::warn_unknown_ext) << Kind; + } + + D->addAttr(::new (S.Context) LibraryExtensionAttr(S.Context, AL, Kind)); +} + static bool isGlobalVar(const Decl *D) { if (const auto *S = dyn_cast(D)) return S->hasGlobalStorage(); @@ -8954,6 +8967,9 @@ case ParsedAttr::AT_Error: handleErrorAttr(S, D, AL); break; + case ParsedAttr::AT_LibraryExtension: + handleLibraryExtensionAttr(S, D, AL); + break; case ParsedAttr::AT_DiagnoseIf: handleDiagnoseIfAttr(S, D, AL); break; diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1560,6 +1560,7 @@ case AR_Deprecated: // Omitting a deprecated constant is ok; it should never materialize. case AR_Unavailable: + case AR_Extension: continue; case AR_NotYetIntroduced: diff --git a/clang/test/SemaCXX/attr-library-extension.cpp b/clang/test/SemaCXX/attr-library-extension.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/attr-library-extension.cpp @@ -0,0 +1,130 @@ +// RUN: %clang_cc1 -Wno-unused-value -std=c++03 -verify=cxx03,cxx11,cxx14,cxx17,cxx20,cxx23,gnu -fsyntax-only %s -DGNUAttr +// RUN: %clang_cc1 -Wno-unused-value -std=c++11 -verify=cxx11,cxx14,cxx17,cxx20,cxx23,gnu -fsyntax-only %s +// RUN: %clang_cc1 -Wno-unused-value -std=c++14 -verify=cxx14,cxx17,cxx20,cxx23,gnu -fsyntax-only %s +// RUN: %clang_cc1 -Wno-unused-value -std=c++17 -verify=cxx17,cxx20,cxx23,gnu -fsyntax-only %s +// RUN: %clang_cc1 -Wno-unused-value -std=c++20 -verify=cxx20,cxx23,gnu -fsyntax-only %s +// RUN: %clang_cc1 -Wno-unused-value -std=c++23 -verify=cxx23,gnu -fsyntax-only %s +// RUN: %clang_cc1 -Wno-unused-value -std=c++26 -verify=gnu -fsyntax-only %s + +#ifdef GNUAttr +#define EXTENSION(name) __attribute__((library_extension(name))) +#else +#define EXTENSION(name) [[clang::library_extension(name)]] +#endif + +struct EXTENSION("C++11") StructCxx11Ext {}; // cxx03-note {{'StructCxx11Ext' has been explicitly marked as an extension here}} +struct EXTENSION("C++14") StructCxx14Ext {}; // cxx11-note {{'StructCxx14Ext' has been explicitly marked as an extension here}} +struct EXTENSION("C++17") StructCxx17Ext {}; // cxx14-note {{'StructCxx17Ext' has been explicitly marked as an extension here}} +struct EXTENSION("C++20") StructCxx20Ext {}; // cxx17-note {{'StructCxx20Ext' has been explicitly marked as an extension here}} +struct EXTENSION("C++23") StructCxx23Ext {}; // cxx20-note {{'StructCxx23Ext' has been explicitly marked as an extension here}} +struct EXTENSION("C++26") StructCxx26Ext {}; // cxx23-note {{'StructCxx26Ext' has been explicitly marked as an extension here}} +struct EXTENSION("GNU") GNUExt {}; // gnu-note {{'GNUExt' has been explicitly marked as an extension here}} + +void consume(StructCxx11Ext); // cxx03-warning {{'StructCxx11Ext' is a C++11 extension}} +void consume(StructCxx14Ext); // cxx11-warning {{'StructCxx14Ext' is a C++14 extension}} +void consume(StructCxx17Ext); // cxx14-warning {{'StructCxx17Ext' is a C++17 extension}} +void consume(StructCxx20Ext); // cxx17-warning {{'StructCxx20Ext' is a C++20 extension}} +void consume(StructCxx23Ext); // cxx20-warning {{'StructCxx23Ext' is a C++23 extension}} +void consume(StructCxx26Ext); // cxx23-warning {{'StructCxx26Ext' is a C++2c extension}} +void consume(GNUExt); // gnu-warning {{'GNUExt' is a GNU extension}} + +namespace EXTENSION("C++11") NSCxx11Ext { // cxx03-note {{'NSCxx11Ext' has been explicitly marked as an extension here}} + struct S {}; +} +void consume(NSCxx11Ext::S); // cxx03-warning {{'NSCxx11Ext' is a C++11 extension}} + +namespace EXTENSION("C++14") NSCxx14Ext { // cxx11-note {{'NSCxx14Ext' has been explicitly marked as an extension here}} + struct S {}; +} +void consume(NSCxx14Ext::S); // cxx11-warning {{'NSCxx14Ext' is a C++14 extension}} + +namespace EXTENSION("C++17") NSCxx17Ext { // cxx14-note {{'NSCxx17Ext' has been explicitly marked as an extension here}} + struct S {}; +} +void consume(NSCxx17Ext::S); // cxx14-warning {{'NSCxx17Ext' is a C++17 extension}} + +namespace EXTENSION("C++20") NSCxx20Ext { // cxx17-note {{'NSCxx20Ext' has been explicitly marked as an extension here}} + struct S {}; +} +void consume(NSCxx20Ext::S); // cxx17-warning {{'NSCxx20Ext' is a C++20 extension}} + +namespace EXTENSION("C++23") NSCxx23Ext { // cxx20-note {{'NSCxx23Ext' has been explicitly marked as an extension here}} + struct S {}; +} +void consume(NSCxx23Ext::S); // cxx20-warning {{'NSCxx23Ext' is a C++23 extension}} + +namespace EXTENSION("C++26") NSCxx26Ext { // cxx23-note {{'NSCxx26Ext' has been explicitly marked as an extension here}} + struct S {}; +} +void consume(NSCxx26Ext::S); // cxx23-warning {{'NSCxx26Ext' is a C++2c extension}} + +namespace EXTENSION("GNU") NSGNUExt { // gnu-note {{'NSGNUExt' has been explicitly marked as an extension here}} + struct S {}; +} +void consume(NSGNUExt::S); // gnu-warning {{'NSGNUExt' is a GNU extension}} + +EXTENSION("C++11") void fcxx11(); // cxx03-note {{'fcxx11' has been explicitly marked as an extension here}} +EXTENSION("C++14") void fcxx14(); // cxx11-note {{'fcxx14' has been explicitly marked as an extension here}} +EXTENSION("C++17") void fcxx17(); // cxx14-note {{'fcxx17' has been explicitly marked as an extension here}} +EXTENSION("C++20") void fcxx20(); // cxx17-note {{'fcxx20' has been explicitly marked as an extension here}} +EXTENSION("C++23") void fcxx23(); // cxx20-note {{'fcxx23' has been explicitly marked as an extension here}} +EXTENSION("C++26") void fcxx26(); // cxx23-note {{'fcxx26' has been explicitly marked as an extension here}} +EXTENSION("GNU") void fgnu(); // gnu-note {{'fgnu' has been explicitly marked as an extension here}} + +void call() { + fcxx11(); // cxx03-warning {{'fcxx11' is a C++11 extension}} + fcxx14(); // cxx11-warning {{'fcxx14' is a C++14 extension}} + fcxx17(); // cxx14-warning {{'fcxx17' is a C++17 extension}} + fcxx20(); // cxx17-warning {{'fcxx20' is a C++20 extension}} + fcxx23(); // cxx20-warning {{'fcxx23' is a C++23 extension}} + fcxx26(); // cxx23-warning {{'fcxx26' is a C++2c extension}} + fgnu(); // gnu-warning {{'fgnu' is a GNU extension}} +} + +EXTENSION("C++11") int vcxx11; // cxx03-note {{'vcxx11' has been explicitly marked as an extension here}} +EXTENSION("C++14") int vcxx14; // cxx11-note {{'vcxx14' has been explicitly marked as an extension here}} +EXTENSION("C++17") int vcxx17; // cxx14-note {{'vcxx17' has been explicitly marked as an extension here}} +EXTENSION("C++20") int vcxx20; // cxx17-note {{'vcxx20' has been explicitly marked as an extension here}} +EXTENSION("C++23") int vcxx23; // cxx20-note {{'vcxx23' has been explicitly marked as an extension here}} +EXTENSION("C++26") int vcxx26; // cxx23-note {{'vcxx26' has been explicitly marked as an extension here}} +EXTENSION("GNU") int vgnu; // gnu-note {{'vgnu' has been explicitly marked as an extension here}} + +void access() { + vcxx11; // cxx03-warning {{'vcxx11' is a C++11 extension}} + vcxx14; // cxx11-warning {{'vcxx14' is a C++14 extension}} + vcxx17; // cxx14-warning {{'vcxx17' is a C++17 extension}} + vcxx20; // cxx17-warning {{'vcxx20' is a C++20 extension}} + vcxx23; // cxx20-warning {{'vcxx23' is a C++23 extension}} + vcxx26; // cxx23-warning {{'vcxx26' is a C++2c extension}} + vgnu; // gnu-warning {{'vgnu' is a GNU extension}} +} + +template +class EXTENSION("C++11") TemplateCxx11Ext {}; // cxx03-note {{'TemplateCxx11Ext' has been explicitly marked as an extension here}} + +void consume(TemplateCxx11Ext); // cxx03-warning {{'TemplateCxx11Ext' is a C++11 extension}} + +template +class EXTENSION("C++14") TemplateCxx14Ext {}; // cxx11-note {{'TemplateCxx14Ext' has been explicitly marked as an extension here}} + +void consume(TemplateCxx14Ext); // cxx11-warning {{'TemplateCxx14Ext' is a C++14 extension}} + +template +class EXTENSION("C++17") TemplateCxx17Ext {}; // cxx14-note {{'TemplateCxx17Ext' has been explicitly marked as an extension here}} + +void consume(TemplateCxx17Ext); // cxx14-warning {{'TemplateCxx17Ext' is a C++17 extension}} + +template +class EXTENSION("C++20") TemplateCxx20Ext {}; // cxx17-note {{'TemplateCxx20Ext' has been explicitly marked as an extension here}} + +void consume(TemplateCxx20Ext); // cxx17-warning {{'TemplateCxx20Ext' is a C++20 extension}} + +template +class EXTENSION("C++23") TemplateCxx23Ext {}; // cxx20-note {{'TemplateCxx23Ext' has been explicitly marked as an extension here}} + +void consume(TemplateCxx23Ext); // cxx20-warning {{'TemplateCxx23Ext' is a C++23 extension}} + +template +class EXTENSION("C++26") TemplateCxx26Ext {}; // cxx23-note {{'TemplateCxx26Ext' has been explicitly marked as an extension here}} + +void consume(TemplateCxx26Ext); // cxx23-warning {{'TemplateCxx26Ext' is a C++2c extension}} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -8280,6 +8280,7 @@ switch (D->getAvailability()) { case AR_Available: case AR_NotYetIntroduced: + case AR_Extension: if (const EnumConstantDecl *EnumConst = dyn_cast(D)) return getCursorAvailabilityForDecl( cast(EnumConst->getDeclContext()));