llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: cor3ntin (cor3ntin)

<details>
<summary>Changes</summary>

This introduces a way detect the libstdc++ version, use that to enable 
workarounds.
The version is cached.

This should make it easier in the future to find and remove these hacks.

I did not find the need for enabling a hack between or after specific versions, 
so it's left as a future exercise.

We can extend this fature to other libraries as the need arise.

---
Full diff: https://github.com/llvm/llvm-project/pull/141977.diff


10 Files Affected:

- (modified) clang/include/clang/Lex/Preprocessor.h (+15) 
- (modified) clang/lib/Lex/PPExpressions.cpp (+45) 
- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+1) 
- (modified) clang/lib/Sema/SemaExceptionSpec.cpp (+3) 
- (modified) clang/lib/Sema/SemaInit.cpp (+5-2) 
- (modified) clang/lib/Sema/SemaTemplate.cpp (+3-22) 
- (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+13-11) 
- (modified) clang/test/SemaCXX/libstdcxx_common_type_hack.cpp (+1-1) 
- (modified) clang/test/SemaCXX/libstdcxx_explicit_init_list_hack.cpp (+1-1) 
- (modified) clang/test/SemaCXX/libstdcxx_pair_swap_hack.cpp (+9-9) 


``````````diff
diff --git a/clang/include/clang/Lex/Preprocessor.h 
b/clang/include/clang/Lex/Preprocessor.h
index f2dfd3a349b8b..442d5dd6cb006 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -129,6 +129,12 @@ enum class EmbedResult {
   Empty = 2,    // Corresponds to __STDC_EMBED_EMPTY__
 };
 
+struct CXXStandardLibraryVersionInfo {
+  enum Library { Unknow, LibStdCXX };
+  Library Lib;
+  unsigned Version;
+};
+
 /// Engages in a tight little dance with the lexer to efficiently
 /// preprocess tokens.
 ///
@@ -2706,6 +2712,15 @@ class Preprocessor {
     return IsFileLexer(CurLexer.get(), CurPPLexer);
   }
 
+  
//===--------------------------------------------------------------------===//
+  // Standard Library Identification
+  std::optional<CXXStandardLibraryVersionInfo> CXXStandardLibraryVersion;
+
+public:
+  std::optional<unsigned> getStdLibCxxVersion();
+  bool NeedsStdLibCxxWorkaroundBefore(unsigned FixedVersion);
+
+private:
   
//===--------------------------------------------------------------------===//
   // Caching stuff.
   void CachingLex(Token &Result);
diff --git a/clang/lib/Lex/PPExpressions.cpp b/clang/lib/Lex/PPExpressions.cpp
index cf7e32bee2e71..2a40b2c93a853 100644
--- a/clang/lib/Lex/PPExpressions.cpp
+++ b/clang/lib/Lex/PPExpressions.cpp
@@ -979,3 +979,48 @@ Preprocessor::EvaluateDirectiveExpression(IdentifierInfo 
*&IfNDefMacro,
   return EvaluateDirectiveExpression(IfNDefMacro, Tok, EvaluatedDefined,
                                      CheckForEoD);
 }
+
+static std::optional<CXXStandardLibraryVersionInfo>
+getCXXStandardLibraryVersion(Preprocessor &PP, StringRef MacroName,
+                             CXXStandardLibraryVersionInfo::Library Lib) {
+  MacroInfo *Macro = PP.getMacroInfo(PP.getIdentifierInfo(MacroName));
+
+  if (!Macro || Macro->getNumTokens() != 1)
+    return std::nullopt;
+
+  const Token &RevisionDateTok = Macro->getReplacementToken(0);
+
+  bool Invalid = false;
+  llvm::SmallVector<char, 10> Buffer;
+  llvm::StringRef RevisionDate =
+      PP.getSpelling(RevisionDateTok, Buffer, &Invalid);
+  if (!Invalid) {
+    unsigned Value;
+    // We don't use NumericParser to avoid diagnostics
+    if (!RevisionDate.consumeInteger(10, Value))
+      return CXXStandardLibraryVersionInfo{
+          CXXStandardLibraryVersionInfo::LibStdCXX, Value};
+  }
+  return CXXStandardLibraryVersionInfo{CXXStandardLibraryVersionInfo::Unknow,
+                                       0};
+}
+
+std::optional<unsigned> Preprocessor::getStdLibCxxVersion() {
+  if (!CXXStandardLibraryVersion)
+    CXXStandardLibraryVersion = getCXXStandardLibraryVersion(
+        *this, "__GLIBCXX__", CXXStandardLibraryVersionInfo::LibStdCXX);
+  if (!CXXStandardLibraryVersion)
+    return std::nullopt;
+
+  if (CXXStandardLibraryVersion->Lib ==
+      CXXStandardLibraryVersionInfo::LibStdCXX)
+    return CXXStandardLibraryVersion->Version;
+  return std::nullopt;
+}
+
+bool Preprocessor::NeedsStdLibCxxWorkaroundBefore(unsigned FixedVersion) {
+  std::optional<unsigned> Ver = getStdLibCxxVersion();
+  if (!Ver)
+    return false;
+  return *Ver < FixedVersion;
+}
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 4a735992cec68..8d8108ee33c11 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -13198,6 +13198,7 @@ NamedDecl *Sema::BuildUsingDeclaration(
     if (getLangOpts().CPlusPlus14 && II && II->isStr("gets") &&
         CurContext->isStdNamespace() &&
         isa<TranslationUnitDecl>(LookupContext) &&
+        PP.NeedsStdLibCxxWorkaroundBefore(20161221) &&
         getSourceManager().isInSystemHeader(UsingLoc))
       return nullptr;
     UsingValidatorCCC CCC(HasTypenameKeyword, IsInstantiation, 
SS.getScopeRep(),
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp 
b/clang/lib/Sema/SemaExceptionSpec.cpp
index c83eab53891ca..c83bbaa0375a0 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -18,6 +18,7 @@
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/SemaInternal.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include <optional>
@@ -44,6 +45,8 @@ static const FunctionProtoType 
*GetUnderlyingFunction(QualType T)
 bool Sema::isLibstdcxxEagerExceptionSpecHack(const Declarator &D) {
   auto *RD = dyn_cast<CXXRecordDecl>(CurContext);
 
+  if (!getPreprocessor().NeedsStdLibCxxWorkaroundBefore(20160427))
+    return false;
   // All the problem cases are member functions named "swap" within class
   // templates declared directly within namespace std or std::__debug or
   // std::__profile.
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 776cb022e6925..4f9fa40ea7575 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -21,6 +21,7 @@
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/Specifiers.h"
 #include "clang/Basic/TargetInfo.h"
+#include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/Designator.h"
 #include "clang/Sema/EnterExpressionEvaluationContext.h"
 #include "clang/Sema/Initialization.h"
@@ -641,8 +642,10 @@ ExprResult 
InitListChecker::PerformEmptyInit(SourceLocation Loc,
   // in that case. stlport does so too.
   // Look for std::__debug for libstdc++, and for std:: for stlport.
   // This is effectively a compiler-side implementation of LWG2193.
-  if (!InitSeq && EmptyInitList && InitSeq.getFailureKind() ==
-          InitializationSequence::FK_ExplicitConstructor) {
+  if (!InitSeq && EmptyInitList &&
+      InitSeq.getFailureKind() ==
+          InitializationSequence::FK_ExplicitConstructor &&
+      SemaRef.getPreprocessor().NeedsStdLibCxxWorkaroundBefore(20140422)) {
     OverloadCandidateSet::iterator Best;
     OverloadingResult O =
         InitSeq.getFailedCandidateSet()
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 10e7823542f0b..6638cbda64a41 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4444,16 +4444,8 @@ static bool IsLibstdcxxStdFormatKind(Preprocessor &PP, 
VarDecl *Var) {
       !Var->getDeclContext()->isStdNamespace())
     return false;
 
-  MacroInfo *MacroGLIBCXX =
-      PP.getMacroInfo(PP.getIdentifierInfo("__GLIBCXX__"));
-
-  if (!MacroGLIBCXX || MacroGLIBCXX->getNumTokens() != 1)
-    return false;
-
-  const Token &RevisionDateTok = MacroGLIBCXX->getReplacementToken(0);
-  bool Invalid = false;
-  std::string RevisionDate = PP.getSpelling(RevisionDateTok, &Invalid);
-
+  // Checking old versions of libstdc++ is not needed because 15.1 is the first
+  // release in which users can access std::format_kind.
   // We can use 20250520 as the final date, see the following commits.
   // GCC releases/gcc-15 branch:
   // https://gcc.gnu.org/g:fedf81ef7b98e5c9ac899b8641bb670746c51205
@@ -4461,18 +4453,7 @@ static bool IsLibstdcxxStdFormatKind(Preprocessor &PP, 
VarDecl *Var) {
   // GCC master branch:
   // https://gcc.gnu.org/g:9361966d80f625c5accc25cbb439f0278dd8b278
   // https://gcc.gnu.org/g:c65725eccbabf3b9b5965f27fff2d3b9f6c75930
-  StringRef FixDate = "20250520";
-
-  if (Invalid)
-    return false;
-
-  // The format of the revision date is in compressed ISO date format.
-  // See https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_macros.html
-  // So we can use string comparison.
-  //
-  // Checking old versions of libstdc++ is not needed because 15.1 is the first
-  // release in which users can access std::format_kind.
-  return RevisionDate.size() == 8 && RevisionDate < FixDate;
+  return PP.NeedsStdLibCxxWorkaroundBefore(20250520);
 }
 } // end anonymous namespace
 
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 174c8fc59e4fa..e848372a54bd3 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1449,17 +1449,19 @@ Decl 
*TemplateDeclInstantiator::InstantiateTypedefNameDecl(TypedefNameDecl *D,
   // happen to be processing that implementation, fake up the g++ ?:
   // semantics. See LWG issue 2141 for more information on the bug.  The bugs
   // are fixed in g++ and libstdc++ 4.9.0 (2014-04-22).
-  const DecltypeType *DT = DI->getType()->getAs<DecltypeType>();
-  CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D->getDeclContext());
-  if (DT && RD && isa<ConditionalOperator>(DT->getUnderlyingExpr()) &&
-      DT->isReferenceType() &&
-      RD->getEnclosingNamespaceContext() == SemaRef.getStdNamespace() &&
-      RD->getIdentifier() && RD->getIdentifier()->isStr("common_type") &&
-      D->getIdentifier() && D->getIdentifier()->isStr("type") &&
-      SemaRef.getSourceManager().isInSystemHeader(D->getBeginLoc()))
-    // Fold it to the (non-reference) type which g++ would have produced.
-    DI = SemaRef.Context.getTrivialTypeSourceInfo(
-      DI->getType().getNonReferenceType());
+  if (SemaRef.getPreprocessor().NeedsStdLibCxxWorkaroundBefore(20140422)) {
+    const DecltypeType *DT = DI->getType()->getAs<DecltypeType>();
+    CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D->getDeclContext());
+    if (DT && RD && isa<ConditionalOperator>(DT->getUnderlyingExpr()) &&
+        DT->isReferenceType() &&
+        RD->getEnclosingNamespaceContext() == SemaRef.getStdNamespace() &&
+        RD->getIdentifier() && RD->getIdentifier()->isStr("common_type") &&
+        D->getIdentifier() && D->getIdentifier()->isStr("type") &&
+        SemaRef.getSourceManager().isInSystemHeader(D->getBeginLoc()))
+      // Fold it to the (non-reference) type which g++ would have produced.
+      DI = SemaRef.Context.getTrivialTypeSourceInfo(
+          DI->getType().getNonReferenceType());
+  }
 
   // Create the new typedef
   TypedefNameDecl *Typedef;
diff --git a/clang/test/SemaCXX/libstdcxx_common_type_hack.cpp 
b/clang/test/SemaCXX/libstdcxx_common_type_hack.cpp
index e9cb22f9dabfa..0e7ba5d6ef0c9 100644
--- a/clang/test/SemaCXX/libstdcxx_common_type_hack.cpp
+++ b/clang/test/SemaCXX/libstdcxx_common_type_hack.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify
+// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -D__GLIBCXX__=20100000L
 
 // This is a test for an egregious hack in Clang that works around
 // an issue with GCC's <type_traits> implementation. std::common_type
diff --git a/clang/test/SemaCXX/libstdcxx_explicit_init_list_hack.cpp 
b/clang/test/SemaCXX/libstdcxx_explicit_init_list_hack.cpp
index f9e0a5c0a1f02..c85af1b1cd567 100644
--- a/clang/test/SemaCXX/libstdcxx_explicit_init_list_hack.cpp
+++ b/clang/test/SemaCXX/libstdcxx_explicit_init_list_hack.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -Wsystem-headers %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -Wsystem-headers 
-D__GLIBCXX__=20100000L %s
 
 // libstdc++4.6 in debug mode has explicit default constructors.
 // stlport has this for all containers.
diff --git a/clang/test/SemaCXX/libstdcxx_pair_swap_hack.cpp 
b/clang/test/SemaCXX/libstdcxx_pair_swap_hack.cpp
index dff599b6d5b66..6b8ca4f740914 100644
--- a/clang/test/SemaCXX/libstdcxx_pair_swap_hack.cpp
+++ b/clang/test/SemaCXX/libstdcxx_pair_swap_hack.cpp
@@ -7,20 +7,20 @@
 // The same problem afflicts a bunch of other class templates. Those
 // affected are array, pair, priority_queue, stack, and queue.
 
-// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -DCLASS=array
-// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -DCLASS=array -DPR28423
-// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -DCLASS=pair
-// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -DCLASS=priority_queue
-// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -DCLASS=stack
-// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -DCLASS=queue
+// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=array
+// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=array -DPR28423
+// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=pair
+// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=priority_queue
+// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=stack
+// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=queue
 //
-// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -DCLASS=array -DNAMESPACE=__debug
-// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -DCLASS=array -DNAMESPACE=__profile
+// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=array -DNAMESPACE=__debug
+// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=array -DNAMESPACE=__profile
 
 // MSVC's standard library uses a very similar pattern that relies on delayed
 // parsing of exception specifications.
 //
-// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -DCLASS=array -DMSVC
+// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions 
-fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=array -DMSVC
 
 #ifdef BE_THE_HEADER
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/141977
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to