philnik created this revision.
philnik added reviewers: aaron.ballman, erichkeane.
Herald added a subscriber: arphaman.
Herald added a project: All.
philnik requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
This allows standard libraries to mark symbols as extensions, so the compiler
can generate extension warnings when they are used.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D157572
Files:
clang/include/clang/AST/DeclBase.h
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/AST/DeclBase.cpp
clang/lib/Sema/CodeCompleteConsumer.cpp
clang/lib/Sema/SemaAvailability.cpp
clang/lib/Sema/SemaDeclAttr.cpp
clang/lib/Sema/SemaStmt.cpp
clang/test/SemaCXX/attr-library-extension.cpp
clang/tools/libclang/CIndex.cpp
Index: clang/tools/libclang/CIndex.cpp
===================================================================
--- clang/tools/libclang/CIndex.cpp
+++ 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<EnumConstantDecl>(D))
return getCursorAvailabilityForDecl(
cast<Decl>(EnumConst->getDeclContext()));
Index: clang/test/SemaCXX/attr-library-extension.cpp
===================================================================
--- /dev/null
+++ 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>
+class EXTENSION("C++11") TemplateCxx11Ext {}; // cxx03-note {{'TemplateCxx11Ext<void>' has been explicitly marked as an extension here}}
+
+void consume(TemplateCxx11Ext<void>); // cxx03-warning {{'TemplateCxx11Ext<void>' is a C++11 extension}}
+
+template <class>
+class EXTENSION("C++14") TemplateCxx14Ext {}; // cxx11-note {{'TemplateCxx14Ext<void>' has been explicitly marked as an extension here}}
+
+void consume(TemplateCxx14Ext<void>); // cxx11-warning {{'TemplateCxx14Ext<void>' is a C++14 extension}}
+
+template <class>
+class EXTENSION("C++17") TemplateCxx17Ext {}; // cxx14-note {{'TemplateCxx17Ext<void>' has been explicitly marked as an extension here}}
+
+void consume(TemplateCxx17Ext<void>); // cxx14-warning {{'TemplateCxx17Ext<void>' is a C++17 extension}}
+
+template <class>
+class EXTENSION("C++20") TemplateCxx20Ext {}; // cxx17-note {{'TemplateCxx20Ext<void>' has been explicitly marked as an extension here}}
+
+void consume(TemplateCxx20Ext<void>); // cxx17-warning {{'TemplateCxx20Ext<void>' is a C++20 extension}}
+
+template <class>
+class EXTENSION("C++23") TemplateCxx23Ext {}; // cxx20-note {{'TemplateCxx23Ext<void>' has been explicitly marked as an extension here}}
+
+void consume(TemplateCxx23Ext<void>); // cxx20-warning {{'TemplateCxx23Ext<void>' is a C++23 extension}}
+
+template <class>
+class EXTENSION("C++26") TemplateCxx26Ext {}; // cxx23-note {{'TemplateCxx26Ext<void>' has been explicitly marked as an extension here}}
+
+void consume(TemplateCxx26Ext<void>); // cxx23-warning {{'TemplateCxx26Ext<void>' is a C++2c extension}}
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ 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:
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ 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<VarDecl>(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;
Index: clang/lib/Sema/SemaAvailability.cpp
===================================================================
--- clang/lib/Sema/SemaAvailability.cpp
+++ clang/lib/Sema/SemaAvailability.cpp
@@ -419,6 +419,31 @@
}
return;
}
+ case AR_Extension: {
+ assert(Message.empty());
+ auto ExtKind = OffendingDecl->getAttr<LibraryExtensionAttr>()->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<LibraryExtensionAttr>())
+ NoteLocation = AL->getLocation();
+
+ break;
+ }
+
case AR_Deprecated:
diag = !ObjCPropertyAccess ? diag::warn_deprecated
: diag::warn_property_method_deprecated;
Index: clang/lib/Sema/CodeCompleteConsumer.cpp
===================================================================
--- clang/lib/Sema/CodeCompleteConsumer.cpp
+++ 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;
Index: clang/lib/AST/DeclBase.cpp
===================================================================
--- clang/lib/AST/DeclBase.cpp
+++ clang/lib/AST/DeclBase.cpp
@@ -656,6 +656,22 @@
continue;
}
+ if (const auto* Extension = dyn_cast<LibraryExtensionAttr>(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<UnavailableAttr>(A)) {
if (Message)
*Message = std::string(Unavailable->getMessage());
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5682,10 +5682,25 @@
InGroup<UnavailableDeclarations>;
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<CXX11>;
+def warn_cxx14_ext : Warning<"%0 is a C++14 extension">,
+ InGroup<CXX14>;
+def warn_cxx17_ext : Warning<"%0 is a C++17 extension">,
+ InGroup<CXX17>;
+def warn_cxx20_ext : Warning<"%0 is a C++20 extension">,
+ InGroup<CXX20>;
+def warn_cxx23_ext : Warning<"%0 is a C++23 extension">,
+ InGroup<CXX23>;
+def warn_cxx26_ext : Warning<"%0 is a C++2c extension">,
+ InGroup<CXX26>;
+def warn_gnu_ext : Warning<"%0 is a GNU extension">,
+ InGroup<GNU>;
def note_implicitly_deleted : Note<
"explicitly defaulted function was implicitly deleted here">;
def warn_not_enough_argument : Warning<
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ 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 CharT, class Traits = char_traits<CharT>>
+ class [[clang::extension("C++17")]] basic_string_view { /*...*/ };
+
+ using string_view = basic_string_view<char>;
+ }
+
+ // 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 = [{
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ 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];
}
-
Index: clang/include/clang/AST/DeclBase.h
===================================================================
--- clang/include/clang/AST/DeclBase.h
+++ 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,
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits