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
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to