Skip to content

Commit 6f7268e

Browse files
authored
[Clang] Improve infrastructure for libstdc++ workarounds (#141977)
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.
1 parent 3a98934 commit 6f7268e

10 files changed

+97
-46
lines changed

clang/include/clang/Lex/Preprocessor.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,12 @@ enum class EmbedResult {
129129
Empty = 2, // Corresponds to __STDC_EMBED_EMPTY__
130130
};
131131

132+
struct CXXStandardLibraryVersionInfo {
133+
enum Library { Unknown, LibStdCXX };
134+
Library Lib;
135+
unsigned Version;
136+
};
137+
132138
/// Engages in a tight little dance with the lexer to efficiently
133139
/// preprocess tokens.
134140
///
@@ -2706,6 +2712,15 @@ class Preprocessor {
27062712
return IsFileLexer(CurLexer.get(), CurPPLexer);
27072713
}
27082714

2715+
//===--------------------------------------------------------------------===//
2716+
// Standard Library Identification
2717+
std::optional<CXXStandardLibraryVersionInfo> CXXStandardLibraryVersion;
2718+
2719+
public:
2720+
std::optional<unsigned> getStdLibCxxVersion();
2721+
bool NeedsStdLibCxxWorkaroundBefore(unsigned FixedVersion);
2722+
2723+
private:
27092724
//===--------------------------------------------------------------------===//
27102725
// Caching stuff.
27112726
void CachingLex(Token &Result);

clang/lib/Lex/PPExpressions.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -979,3 +979,49 @@ Preprocessor::EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro,
979979
return EvaluateDirectiveExpression(IfNDefMacro, Tok, EvaluatedDefined,
980980
CheckForEoD);
981981
}
982+
983+
static std::optional<CXXStandardLibraryVersionInfo>
984+
getCXXStandardLibraryVersion(Preprocessor &PP, StringRef MacroName,
985+
CXXStandardLibraryVersionInfo::Library Lib) {
986+
MacroInfo *Macro = PP.getMacroInfo(PP.getIdentifierInfo(MacroName));
987+
988+
if (!Macro || Macro->getNumTokens() != 1 || !Macro->isObjectLike())
989+
return std::nullopt;
990+
991+
const Token &RevisionDateTok = Macro->getReplacementToken(0);
992+
993+
bool Invalid = false;
994+
llvm::SmallVector<char, 10> Buffer;
995+
llvm::StringRef RevisionDate =
996+
PP.getSpelling(RevisionDateTok, Buffer, &Invalid);
997+
if (!Invalid) {
998+
unsigned Value;
999+
// We don't use NumericParser to avoid diagnostics
1000+
if (!RevisionDate.consumeInteger(10, Value))
1001+
return CXXStandardLibraryVersionInfo{Lib, Value};
1002+
}
1003+
return CXXStandardLibraryVersionInfo{CXXStandardLibraryVersionInfo::Unknown,
1004+
0};
1005+
}
1006+
1007+
std::optional<unsigned> Preprocessor::getStdLibCxxVersion() {
1008+
if (!CXXStandardLibraryVersion)
1009+
CXXStandardLibraryVersion = getCXXStandardLibraryVersion(
1010+
*this, "__GLIBCXX__", CXXStandardLibraryVersionInfo::LibStdCXX);
1011+
if (!CXXStandardLibraryVersion)
1012+
return std::nullopt;
1013+
1014+
if (CXXStandardLibraryVersion->Lib ==
1015+
CXXStandardLibraryVersionInfo::LibStdCXX)
1016+
return CXXStandardLibraryVersion->Version;
1017+
return std::nullopt;
1018+
}
1019+
1020+
bool Preprocessor::NeedsStdLibCxxWorkaroundBefore(unsigned FixedVersion) {
1021+
assert(FixedVersion >= 2000'00'00 && FixedVersion <= 2100'00'00 &&
1022+
"invalid value for __GLIBCXX__");
1023+
std::optional<unsigned> Ver = getStdLibCxxVersion();
1024+
if (!Ver)
1025+
return false;
1026+
return *Ver < FixedVersion;
1027+
}

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13198,6 +13198,7 @@ NamedDecl *Sema::BuildUsingDeclaration(
1319813198
if (getLangOpts().CPlusPlus14 && II && II->isStr("gets") &&
1319913199
CurContext->isStdNamespace() &&
1320013200
isa<TranslationUnitDecl>(LookupContext) &&
13201+
PP.NeedsStdLibCxxWorkaroundBefore(2016'12'21) &&
1320113202
getSourceManager().isInSystemHeader(UsingLoc))
1320213203
return nullptr;
1320313204
UsingValidatorCCC CCC(HasTypenameKeyword, IsInstantiation, SS.getScopeRep(),

clang/lib/Sema/SemaExceptionSpec.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "clang/AST/TypeLoc.h"
1919
#include "clang/Basic/Diagnostic.h"
2020
#include "clang/Basic/SourceManager.h"
21+
#include "clang/Lex/Preprocessor.h"
2122
#include "clang/Sema/SemaInternal.h"
2223
#include "llvm/ADT/SmallPtrSet.h"
2324
#include <optional>
@@ -44,6 +45,8 @@ static const FunctionProtoType *GetUnderlyingFunction(QualType T)
4445
bool Sema::isLibstdcxxEagerExceptionSpecHack(const Declarator &D) {
4546
auto *RD = dyn_cast<CXXRecordDecl>(CurContext);
4647

48+
if (!getPreprocessor().NeedsStdLibCxxWorkaroundBefore(2016'04'27))
49+
return false;
4750
// All the problem cases are member functions named "swap" within class
4851
// templates declared directly within namespace std or std::__debug or
4952
// std::__profile.

clang/lib/Sema/SemaInit.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "clang/Basic/SourceManager.h"
2222
#include "clang/Basic/Specifiers.h"
2323
#include "clang/Basic/TargetInfo.h"
24+
#include "clang/Lex/Preprocessor.h"
2425
#include "clang/Sema/Designator.h"
2526
#include "clang/Sema/EnterExpressionEvaluationContext.h"
2627
#include "clang/Sema/Initialization.h"
@@ -641,8 +642,10 @@ ExprResult InitListChecker::PerformEmptyInit(SourceLocation Loc,
641642
// in that case. stlport does so too.
642643
// Look for std::__debug for libstdc++, and for std:: for stlport.
643644
// This is effectively a compiler-side implementation of LWG2193.
644-
if (!InitSeq && EmptyInitList && InitSeq.getFailureKind() ==
645-
InitializationSequence::FK_ExplicitConstructor) {
645+
if (!InitSeq && EmptyInitList &&
646+
InitSeq.getFailureKind() ==
647+
InitializationSequence::FK_ExplicitConstructor &&
648+
SemaRef.getPreprocessor().NeedsStdLibCxxWorkaroundBefore(2014'04'22)) {
646649
OverloadCandidateSet::iterator Best;
647650
OverloadingResult O =
648651
InitSeq.getFailedCandidateSet()

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4444,35 +4444,16 @@ static bool IsLibstdcxxStdFormatKind(Preprocessor &PP, VarDecl *Var) {
44444444
!Var->getDeclContext()->isStdNamespace())
44454445
return false;
44464446

4447-
MacroInfo *MacroGLIBCXX =
4448-
PP.getMacroInfo(PP.getIdentifierInfo("__GLIBCXX__"));
4449-
4450-
if (!MacroGLIBCXX || MacroGLIBCXX->getNumTokens() != 1)
4451-
return false;
4452-
4453-
const Token &RevisionDateTok = MacroGLIBCXX->getReplacementToken(0);
4454-
bool Invalid = false;
4455-
std::string RevisionDate = PP.getSpelling(RevisionDateTok, &Invalid);
4456-
4447+
// Checking old versions of libstdc++ is not needed because 15.1 is the first
4448+
// release in which users can access std::format_kind.
44574449
// We can use 20250520 as the final date, see the following commits.
44584450
// GCC releases/gcc-15 branch:
44594451
// https://gcc.gnu.org/g:fedf81ef7b98e5c9ac899b8641bb670746c51205
44604452
// https://gcc.gnu.org/g:53680c1aa92d9f78e8255fbf696c0ed36f160650
44614453
// GCC master branch:
44624454
// https://gcc.gnu.org/g:9361966d80f625c5accc25cbb439f0278dd8b278
44634455
// https://gcc.gnu.org/g:c65725eccbabf3b9b5965f27fff2d3b9f6c75930
4464-
StringRef FixDate = "20250520";
4465-
4466-
if (Invalid)
4467-
return false;
4468-
4469-
// The format of the revision date is in compressed ISO date format.
4470-
// See https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_macros.html
4471-
// So we can use string comparison.
4472-
//
4473-
// Checking old versions of libstdc++ is not needed because 15.1 is the first
4474-
// release in which users can access std::format_kind.
4475-
return RevisionDate.size() == 8 && RevisionDate < FixDate;
4456+
return PP.NeedsStdLibCxxWorkaroundBefore(2025'05'20);
44764457
}
44774458
} // end anonymous namespace
44784459

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1449,17 +1449,19 @@ Decl *TemplateDeclInstantiator::InstantiateTypedefNameDecl(TypedefNameDecl *D,
14491449
// happen to be processing that implementation, fake up the g++ ?:
14501450
// semantics. See LWG issue 2141 for more information on the bug. The bugs
14511451
// are fixed in g++ and libstdc++ 4.9.0 (2014-04-22).
1452-
const DecltypeType *DT = DI->getType()->getAs<DecltypeType>();
1453-
CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D->getDeclContext());
1454-
if (DT && RD && isa<ConditionalOperator>(DT->getUnderlyingExpr()) &&
1455-
DT->isReferenceType() &&
1456-
RD->getEnclosingNamespaceContext() == SemaRef.getStdNamespace() &&
1457-
RD->getIdentifier() && RD->getIdentifier()->isStr("common_type") &&
1458-
D->getIdentifier() && D->getIdentifier()->isStr("type") &&
1459-
SemaRef.getSourceManager().isInSystemHeader(D->getBeginLoc()))
1460-
// Fold it to the (non-reference) type which g++ would have produced.
1461-
DI = SemaRef.Context.getTrivialTypeSourceInfo(
1462-
DI->getType().getNonReferenceType());
1452+
if (SemaRef.getPreprocessor().NeedsStdLibCxxWorkaroundBefore(2014'04'22)) {
1453+
const DecltypeType *DT = DI->getType()->getAs<DecltypeType>();
1454+
CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D->getDeclContext());
1455+
if (DT && RD && isa<ConditionalOperator>(DT->getUnderlyingExpr()) &&
1456+
DT->isReferenceType() &&
1457+
RD->getEnclosingNamespaceContext() == SemaRef.getStdNamespace() &&
1458+
RD->getIdentifier() && RD->getIdentifier()->isStr("common_type") &&
1459+
D->getIdentifier() && D->getIdentifier()->isStr("type") &&
1460+
SemaRef.getSourceManager().isInSystemHeader(D->getBeginLoc()))
1461+
// Fold it to the (non-reference) type which g++ would have produced.
1462+
DI = SemaRef.Context.getTrivialTypeSourceInfo(
1463+
DI->getType().getNonReferenceType());
1464+
}
14631465

14641466
// Create the new typedef
14651467
TypedefNameDecl *Typedef;

clang/test/SemaCXX/libstdcxx_common_type_hack.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify
1+
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -D__GLIBCXX__=20100000L
22

33
// This is a test for an egregious hack in Clang that works around
44
// an issue with GCC's <type_traits> implementation. std::common_type

clang/test/SemaCXX/libstdcxx_explicit_init_list_hack.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -Wsystem-headers %s
1+
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -Wsystem-headers -D__GLIBCXX__=20100000L %s
22

33
// libstdc++4.6 in debug mode has explicit default constructors.
44
// stlport has this for all containers.

clang/test/SemaCXX/libstdcxx_pair_swap_hack.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,20 @@
77
// The same problem afflicts a bunch of other class templates. Those
88
// affected are array, pair, priority_queue, stack, and queue.
99

10-
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -DCLASS=array
11-
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -DCLASS=array -DPR28423
12-
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -DCLASS=pair
13-
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -DCLASS=priority_queue
14-
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -DCLASS=stack
15-
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -DCLASS=queue
10+
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=array
11+
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=array -DPR28423
12+
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=pair
13+
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=priority_queue
14+
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=stack
15+
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=queue
1616
//
17-
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -DCLASS=array -DNAMESPACE=__debug
18-
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -DCLASS=array -DNAMESPACE=__profile
17+
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=array -DNAMESPACE=__debug
18+
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=array -DNAMESPACE=__profile
1919

2020
// MSVC's standard library uses a very similar pattern that relies on delayed
2121
// parsing of exception specifications.
2222
//
23-
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -DCLASS=array -DMSVC
23+
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -D__GLIBCXX__=20100000L -DCLASS=array -DMSVC
2424

2525
#ifdef BE_THE_HEADER
2626

0 commit comments

Comments
 (0)