Skip to content

Commit c9968f4

Browse files
authored
[Clang] Improve infrastructure for libstdc++ workarounds (Reland) (#142592)
Reland with debug traces to try to understand a bug that only happens on one CI configuration === 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 ec96c0c commit c9968f4

10 files changed

+104
-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: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -979,3 +979,56 @@ 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+
if (!Macro)
988+
return std::nullopt;
989+
990+
if (Macro->getNumTokens() != 1 || !Macro->isObjectLike()) {
991+
Macro->dump();
992+
return std::nullopt;
993+
}
994+
995+
const Token &RevisionDateTok = Macro->getReplacementToken(0);
996+
997+
bool Invalid = false;
998+
llvm::SmallVector<char, 10> Buffer;
999+
llvm::StringRef RevisionDate =
1000+
PP.getSpelling(RevisionDateTok, Buffer, &Invalid);
1001+
if (!Invalid) {
1002+
llvm::errs() << RevisionDate << "\n";
1003+
unsigned Value;
1004+
// We don't use NumericParser to avoid diagnostics
1005+
if (!RevisionDate.consumeInteger(10, Value)) {
1006+
llvm::errs() << "Value:" << Value << "\n";
1007+
return CXXStandardLibraryVersionInfo{Lib, Value};
1008+
}
1009+
}
1010+
return CXXStandardLibraryVersionInfo{CXXStandardLibraryVersionInfo::Unknown,
1011+
0};
1012+
}
1013+
1014+
std::optional<unsigned> Preprocessor::getStdLibCxxVersion() {
1015+
if (!CXXStandardLibraryVersion)
1016+
CXXStandardLibraryVersion = getCXXStandardLibraryVersion(
1017+
*this, "__GLIBCXX__", CXXStandardLibraryVersionInfo::LibStdCXX);
1018+
if (!CXXStandardLibraryVersion)
1019+
return std::nullopt;
1020+
1021+
if (CXXStandardLibraryVersion->Lib ==
1022+
CXXStandardLibraryVersionInfo::LibStdCXX)
1023+
return CXXStandardLibraryVersion->Version;
1024+
return std::nullopt;
1025+
}
1026+
1027+
bool Preprocessor::NeedsStdLibCxxWorkaroundBefore(unsigned FixedVersion) {
1028+
assert(FixedVersion >= 2000'00'00 && FixedVersion <= 2100'00'00 &&
1029+
"invalid value for __GLIBCXX__");
1030+
std::optional<unsigned> Ver = getStdLibCxxVersion();
1031+
if (!Ver)
1032+
return false;
1033+
return *Ver < FixedVersion;
1034+
}

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
@@ -1448,17 +1448,19 @@ Decl *TemplateDeclInstantiator::InstantiateTypedefNameDecl(TypedefNameDecl *D,
14481448
// happen to be processing that implementation, fake up the g++ ?:
14491449
// semantics. See LWG issue 2141 for more information on the bug. The bugs
14501450
// are fixed in g++ and libstdc++ 4.9.0 (2014-04-22).
1451-
const DecltypeType *DT = DI->getType()->getAs<DecltypeType>();
1452-
CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D->getDeclContext());
1453-
if (DT && RD && isa<ConditionalOperator>(DT->getUnderlyingExpr()) &&
1454-
DT->isReferenceType() &&
1455-
RD->getEnclosingNamespaceContext() == SemaRef.getStdNamespace() &&
1456-
RD->getIdentifier() && RD->getIdentifier()->isStr("common_type") &&
1457-
D->getIdentifier() && D->getIdentifier()->isStr("type") &&
1458-
SemaRef.getSourceManager().isInSystemHeader(D->getBeginLoc()))
1459-
// Fold it to the (non-reference) type which g++ would have produced.
1460-
DI = SemaRef.Context.getTrivialTypeSourceInfo(
1461-
DI->getType().getNonReferenceType());
1451+
if (SemaRef.getPreprocessor().NeedsStdLibCxxWorkaroundBefore(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());
1463+
}
14621464

14631465
// Create the new typedef
14641466
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 -Wextra -D__GLIBCXX__=20100000L -DCLASS=array
11+
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -Wextra -D__GLIBCXX__=20100000L -DCLASS=array -DPR28423
12+
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -Wextra -D__GLIBCXX__=20100000L -DCLASS=pair
13+
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -Wextra -D__GLIBCXX__=20100000L -DCLASS=priority_queue
14+
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -Wextra -D__GLIBCXX__=20100000L -DCLASS=stack
15+
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -Wextra -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 -Wextra -D__GLIBCXX__=20100000L -DCLASS=array -DNAMESPACE=__debug
18+
// RUN: %clang_cc1 -fsyntax-only %s -std=c++11 -verify -fexceptions -fcxx-exceptions -Wextra -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)