Skip to content

Commit c5301a7

Browse files
committed
Merge commit '2d67cb1a1f2a5e4864c84470454b287d4061fbb2' into llvmspirv_pulldown
2 parents ef8b307 + 2d67cb1 commit c5301a7

File tree

161 files changed

+4130
-2183
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

161 files changed

+4130
-2183
lines changed

clang/docs/analyzer/developer-docs/DebugChecks.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,22 @@ The analyzer contains a number of checkers which can aid in debugging. Enable
99
them by using the "-analyzer-checker=" flag, followed by the name of the
1010
checker.
1111

12+
These checkers are especially useful when analyzing a specific function, using
13+
the `-analyze-function` flag. The flag accepts the function name for C code,
14+
like `-analyze-function=myfunction`.
15+
For C++ code, due to overloading, the function name must include the
16+
parameter list, like `-analyze-function="myfunction(int, _Bool)"`.
17+
18+
Note that `bool` must be spelled as `_Bool` in the parameter list.
19+
Refer to the output of `-analyzer-display-progress` to find the fully qualified
20+
function name.
21+
22+
There are cases when this name can still collide. For example with template
23+
function instances with non-deducible (aka. explicit) template parameters.
24+
In such cases, prefer passing a USR instead of a function name can resolve this
25+
ambiguity, like this: `-analyze-function="c:@S@Window@F@overloaded#I#"`.
26+
27+
Use the `clang-extdef-mapping` tool to find the USR for different functions.
1228

1329
General Analysis Dumpers
1430
========================

clang/include/clang/Basic/BuiltinsX86.td

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,13 @@ let Features = "sse2", Attributes = [NoThrow] in {
216216
def movnti : X86Builtin<"void(int *, int)">;
217217
}
218218

219-
let Features = "sse2", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
220-
def pshufd : X86Builtin<"_Vector<4, int>(_Vector<4, int>, _Constant int)">;
219+
let Features = "sse2", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
221220
def pshuflw : X86Builtin<"_Vector<8, short>(_Vector<8, short>, _Constant int)">;
221+
def pshufd : X86Builtin<"_Vector<4, int>(_Vector<4, int>, _Constant int)">;
222222
def pshufhw : X86Builtin<"_Vector<8, short>(_Vector<8, short>, _Constant int)">;
223+
}
224+
225+
let Features = "sse2", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] in {
223226
def psadbw128 : X86Builtin<"_Vector<2, long long int>(_Vector<16, char>, _Vector<16, char>)">;
224227
def sqrtpd : X86Builtin<"_Vector<2, double>(_Vector<2, double>)">;
225228
def sqrtsd : X86Builtin<"_Vector<2, double>(_Vector<2, double>)">;
@@ -584,9 +587,6 @@ let Features = "avx2", Attributes = [NoThrow, Const, RequiredVectorWidth<256>] i
584587
def pmulhrsw256 : X86Builtin<"_Vector<16, short>(_Vector<16, short>, _Vector<16, short>)">;
585588
def psadbw256 : X86Builtin<"_Vector<4, long long int>(_Vector<32, char>, _Vector<32, char>)">;
586589
def pshufb256 : X86Builtin<"_Vector<32, char>(_Vector<32, char>, _Vector<32, char>)">;
587-
def pshufd256 : X86Builtin<"_Vector<8, int>(_Vector<8, int>, _Constant int)">;
588-
def pshuflw256 : X86Builtin<"_Vector<16, short>(_Vector<16, short>, _Constant int)">;
589-
def pshufhw256 : X86Builtin<"_Vector<16, short>(_Vector<16, short>, _Constant int)">;
590590
def psignb256 : X86Builtin<"_Vector<32, char>(_Vector<32, char>, _Vector<32, char>)">;
591591
def psignw256 : X86Builtin<"_Vector<16, short>(_Vector<16, short>, _Vector<16, short>)">;
592592
def psignd256 : X86Builtin<"_Vector<8, int>(_Vector<8, int>, _Vector<8, int>)">;
@@ -647,6 +647,10 @@ let Features = "avx2", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWi
647647
def packsswb256 : X86Builtin<"_Vector<32, char>(_Vector<16, short>, _Vector<16, short>)">;
648648
def packssdw256 : X86Builtin<"_Vector<16, short>(_Vector<8, int>, _Vector<8, int>)">;
649649
def packuswb256 : X86Builtin<"_Vector<32, char>(_Vector<16, short>, _Vector<16, short>)">;
650+
651+
def pshuflw256 : X86Builtin<"_Vector<16, short>(_Vector<16, short>, _Constant int)">;
652+
def pshufhw256 : X86Builtin<"_Vector<16, short>(_Vector<16, short>, _Constant int)">;
653+
def pshufd256 : X86Builtin<"_Vector<8, int>(_Vector<8, int>, _Constant int)">;
650654
}
651655

652656
let Features = "avx2", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<128>] in {
@@ -1017,6 +1021,7 @@ let Features = "avx512f", Attributes = [NoThrow, Const, RequiredVectorWidth<512>
10171021
let Features = "avx512f", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
10181022
def pmuldq512 : X86Builtin<"_Vector<8, long long int>(_Vector<16, int>, _Vector<16, int>)">;
10191023
def pmuludq512 : X86Builtin<"_Vector<8, long long int>(_Vector<16, int>, _Vector<16, int>)">;
1024+
def pshufd512 : X86Builtin<"_Vector<16, int>(_Vector<16, int>, _Constant int)">;
10201025
}
10211026

10221027
let Features = "avx512f", Attributes = [NoThrow, RequiredVectorWidth<512>] in {
@@ -1990,13 +1995,13 @@ let Features = "avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVect
19901995
}
19911996

19921997
let Features = "avx512bw", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
1993-
def pshufhw512 : X86Builtin<"_Vector<32, short>(_Vector<32, short>, _Constant int)">;
1994-
def pshuflw512 : X86Builtin<"_Vector<32, short>(_Vector<32, short>, _Constant int)">;
19951998
def psllw512 : X86Builtin<"_Vector<32, short>(_Vector<32, short>, _Vector<8, short>)">;
19961999
}
19972000

19982001
let Features = "avx512bw", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
19992002
def psllv32hi : X86Builtin<"_Vector<32, short>(_Vector<32, short>, _Vector<32, short>)">;
2003+
def pshufhw512 : X86Builtin<"_Vector<32, short>(_Vector<32, short>, _Constant int)">;
2004+
def pshuflw512 : X86Builtin<"_Vector<32, short>(_Vector<32, short>, _Constant int)">;
20002005
}
20012006

20022007
let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<256>] in {
@@ -2026,8 +2031,7 @@ let Features = "avx512bw,avx512vl", Attributes = [NoThrow, Const, Constexpr, Req
20262031
def psrlv8hi : X86Builtin<"_Vector<8, short>(_Vector<8, short>, _Vector<8, short>)">;
20272032
}
20282033

2029-
let Features = "avx512f",
2030-
Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
2034+
let Features = "avx512f", Attributes = [NoThrow, Const, Constexpr, RequiredVectorWidth<512>] in {
20312035
def psrlwi512 : X86Builtin<"_Vector<32, short>(_Vector<32, short>, int)">;
20322036
def psrldi512 : X86Builtin<"_Vector<16, int>(_Vector<16, int>, int)">;
20332037
def psrlqi512 : X86Builtin<"_Vector<8, long long int>(_Vector<8, long long int>, int)">;
@@ -3266,7 +3270,6 @@ let Features = "avx512f", Attributes = [NoThrow, Const, RequiredVectorWidth<128>
32663270
}
32673271

32683272
let Features = "avx512f", Attributes = [NoThrow, Const, RequiredVectorWidth<512>] in {
3269-
def pshufd512 : X86Builtin<"_Vector<16, int>(_Vector<16, int>, _Constant int)">;
32703273
def expanddf512_mask : X86Builtin<"_Vector<8, double>(_Vector<8, double>, _Vector<8, double>, unsigned char)">;
32713274
def expanddi512_mask : X86Builtin<"_Vector<8, long long int>(_Vector<8, long long int>, _Vector<8, long long int>, unsigned char)">;
32723275
}

clang/include/clang/CrossTU/CrossTranslationUnit.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ class CrossTranslationUnitContext {
180180
llvm::Expected<const VarDecl *> importDefinition(const VarDecl *VD,
181181
ASTUnit *Unit);
182182

183-
/// Get a name to identify a named decl.
184-
static std::optional<std::string> getLookupName(const NamedDecl *ND);
183+
/// Get a name to identify a decl.
184+
static std::optional<std::string> getLookupName(const Decl *D);
185185

186186
/// Emit diagnostics for the user for potential configuration errors.
187187
void emitCrossTUDiagnostics(const IndexError &IE);

clang/include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class EntryPointStat {
2525
public:
2626
llvm::StringLiteral name() const { return Name; }
2727

28-
static void lockRegistry();
28+
static void lockRegistry(llvm::StringRef CPPFileName);
2929

3030
static void takeSnapshot(const Decl *EntryPoint);
3131
static void dumpStatsAsCSV(llvm::raw_ostream &OS);

clang/lib/AST/ByteCode/InterpBuiltin.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2773,6 +2773,50 @@ static bool interp__builtin_blend(InterpState &S, CodePtr OpPC,
27732773
return true;
27742774
}
27752775

2776+
static bool interp__builtin_ia32_pshuf(InterpState &S, CodePtr OpPC,
2777+
const CallExpr *Call, bool IsShufHW) {
2778+
assert(Call->getNumArgs() == 2 && "masked forms handled via select*");
2779+
APSInt ControlImm = popToAPSInt(S, Call->getArg(1));
2780+
const Pointer &Src = S.Stk.pop<Pointer>();
2781+
const Pointer &Dst = S.Stk.peek<Pointer>();
2782+
2783+
unsigned NumElems = Dst.getNumElems();
2784+
PrimType ElemT = Dst.getFieldDesc()->getPrimType();
2785+
2786+
unsigned ElemBits = static_cast<unsigned>(primSize(ElemT) * 8);
2787+
if (ElemBits != 16 && ElemBits != 32)
2788+
return false;
2789+
2790+
unsigned LaneElts = 128u / ElemBits;
2791+
assert(LaneElts && (NumElems % LaneElts == 0));
2792+
2793+
uint8_t Ctl = static_cast<uint8_t>(ControlImm.getZExtValue());
2794+
2795+
for (unsigned Idx = 0; Idx != NumElems; Idx++) {
2796+
unsigned LaneBase = (Idx / LaneElts) * LaneElts;
2797+
unsigned LaneIdx = Idx % LaneElts;
2798+
unsigned SrcIdx = Idx;
2799+
unsigned Sel = (Ctl >> (2 * LaneIdx)) & 0x3;
2800+
if (ElemBits == 32) {
2801+
SrcIdx = LaneBase + Sel;
2802+
} else {
2803+
constexpr unsigned HalfSize = 4;
2804+
bool InHigh = LaneIdx >= HalfSize;
2805+
if (!IsShufHW && !InHigh) {
2806+
SrcIdx = LaneBase + Sel;
2807+
} else if (IsShufHW && InHigh) {
2808+
unsigned Rel = LaneIdx - HalfSize;
2809+
Sel = (Ctl >> (2 * Rel)) & 0x3;
2810+
SrcIdx = LaneBase + HalfSize + Sel;
2811+
}
2812+
}
2813+
2814+
INT_TYPE_SWITCH_NO_BOOL(ElemT, { Dst.elem<T>(Idx) = Src.elem<T>(SrcIdx); });
2815+
}
2816+
Dst.initializeAllElements();
2817+
return true;
2818+
}
2819+
27762820
static bool interp__builtin_elementwise_triop(
27772821
InterpState &S, CodePtr OpPC, const CallExpr *Call,
27782822
llvm::function_ref<APInt(const APSInt &, const APSInt &, const APSInt &)>
@@ -3661,6 +3705,21 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
36613705
case X86::BI__builtin_ia32_selectpd_512:
36623706
return interp__builtin_select(S, OpPC, Call);
36633707

3708+
case X86::BI__builtin_ia32_pshuflw:
3709+
case X86::BI__builtin_ia32_pshuflw256:
3710+
case X86::BI__builtin_ia32_pshuflw512:
3711+
return interp__builtin_ia32_pshuf(S, OpPC, Call, false);
3712+
3713+
case X86::BI__builtin_ia32_pshufhw:
3714+
case X86::BI__builtin_ia32_pshufhw256:
3715+
case X86::BI__builtin_ia32_pshufhw512:
3716+
return interp__builtin_ia32_pshuf(S, OpPC, Call, true);
3717+
3718+
case X86::BI__builtin_ia32_pshufd:
3719+
case X86::BI__builtin_ia32_pshufd256:
3720+
case X86::BI__builtin_ia32_pshufd512:
3721+
return interp__builtin_ia32_pshuf(S, OpPC, Call, false);
3722+
36643723
case X86::BI__builtin_ia32_kandqi:
36653724
case X86::BI__builtin_ia32_kandhi:
36663725
case X86::BI__builtin_ia32_kandsi:

clang/lib/AST/ExprConstant.cpp

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11633,6 +11633,60 @@ static bool evalPackBuiltin(const CallExpr *E, EvalInfo &Info, APValue &Result,
1163311633
return true;
1163411634
}
1163511635

11636+
static bool evalPshufBuiltin(EvalInfo &Info, const CallExpr *Call,
11637+
bool IsShufHW, APValue &Out) {
11638+
APValue Vec;
11639+
APSInt Imm;
11640+
if (!EvaluateAsRValue(Info, Call->getArg(0), Vec))
11641+
return false;
11642+
if (!EvaluateInteger(Call->getArg(1), Imm, Info))
11643+
return false;
11644+
11645+
const auto *VT = Call->getType()->getAs<VectorType>();
11646+
if (!VT)
11647+
return false;
11648+
11649+
QualType ElemT = VT->getElementType();
11650+
unsigned ElemBits = Info.Ctx.getTypeSize(ElemT);
11651+
unsigned NumElts = VT->getNumElements();
11652+
11653+
unsigned LaneBits = 128u;
11654+
unsigned LaneElts = LaneBits / ElemBits;
11655+
if (!LaneElts || (NumElts % LaneElts) != 0)
11656+
return false;
11657+
11658+
uint8_t Ctl = static_cast<uint8_t>(Imm.getZExtValue());
11659+
11660+
SmallVector<APValue, 32> ResultElements;
11661+
ResultElements.reserve(NumElts);
11662+
11663+
for (unsigned Idx = 0; Idx != NumElts; Idx++) {
11664+
unsigned LaneBase = (Idx / LaneElts) * LaneElts;
11665+
unsigned LaneIdx = Idx % LaneElts;
11666+
unsigned SrcIdx = Idx;
11667+
unsigned Sel = (Ctl >> (2 * LaneIdx)) & 0x3;
11668+
11669+
if (ElemBits == 32) {
11670+
SrcIdx = LaneBase + Sel;
11671+
} else {
11672+
constexpr unsigned HalfSize = 4;
11673+
bool InHigh = LaneIdx >= HalfSize;
11674+
if (!IsShufHW && !InHigh) {
11675+
SrcIdx = LaneBase + Sel;
11676+
} else if (IsShufHW && InHigh) {
11677+
unsigned Rel = LaneIdx - HalfSize;
11678+
Sel = (Ctl >> (2 * Rel)) & 0x3;
11679+
SrcIdx = LaneBase + HalfSize + Sel;
11680+
}
11681+
}
11682+
11683+
ResultElements.push_back(Vec.getVectorElt(SrcIdx));
11684+
}
11685+
11686+
Out = APValue(ResultElements.data(), ResultElements.size());
11687+
return true;
11688+
}
11689+
1163611690
bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
1163711691
if (!IsConstantEvaluatedBuiltinCall(E))
1163811692
return ExprEvaluatorBaseTy::VisitCallExpr(E);
@@ -11886,7 +11940,6 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
1188611940

1188711941
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
1188811942
}
11889-
1189011943
case clang::X86::BI__builtin_ia32_vprotbi:
1189111944
case clang::X86::BI__builtin_ia32_vprotdi:
1189211945
case clang::X86::BI__builtin_ia32_vprotqi:
@@ -12105,6 +12158,34 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
1210512158

1210612159
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
1210712160
}
12161+
12162+
case X86::BI__builtin_ia32_pshuflw:
12163+
case X86::BI__builtin_ia32_pshuflw256:
12164+
case X86::BI__builtin_ia32_pshuflw512: {
12165+
APValue R;
12166+
if (!evalPshufBuiltin(Info, E, false, R))
12167+
return false;
12168+
return Success(R, E);
12169+
}
12170+
12171+
case X86::BI__builtin_ia32_pshufhw:
12172+
case X86::BI__builtin_ia32_pshufhw256:
12173+
case X86::BI__builtin_ia32_pshufhw512: {
12174+
APValue R;
12175+
if (!evalPshufBuiltin(Info, E, true, R))
12176+
return false;
12177+
return Success(R, E);
12178+
}
12179+
12180+
case X86::BI__builtin_ia32_pshufd:
12181+
case X86::BI__builtin_ia32_pshufd256:
12182+
case X86::BI__builtin_ia32_pshufd512: {
12183+
APValue R;
12184+
if (!evalPshufBuiltin(Info, E, false, R))
12185+
return false;
12186+
return Success(R, E);
12187+
}
12188+
1210812189
case Builtin::BI__builtin_elementwise_clzg:
1210912190
case Builtin::BI__builtin_elementwise_ctzg: {
1211012191
APValue SourceLHS;

clang/lib/CrossTU/CrossTranslationUnit.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,9 @@ CrossTranslationUnitContext::CrossTranslationUnitContext(CompilerInstance &CI)
252252
CrossTranslationUnitContext::~CrossTranslationUnitContext() {}
253253

254254
std::optional<std::string>
255-
CrossTranslationUnitContext::getLookupName(const NamedDecl *ND) {
255+
CrossTranslationUnitContext::getLookupName(const Decl *D) {
256256
SmallString<128> DeclUSR;
257-
bool Ret = index::generateUSRForDecl(ND, DeclUSR);
257+
bool Ret = index::generateUSRForDecl(D, DeclUSR);
258258
if (Ret)
259259
return {};
260260
return std::string(DeclUSR);

clang/lib/StaticAnalyzer/Core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ add_clang_library(clangStaticAnalyzerCore
6161
clangBasic
6262
clangCrossTU
6363
clangFrontend
64+
clangIndex
6465
clangLex
6566
clangRewrite
6667
clangToolingCore

clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
#include "clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h"
1010
#include "clang/AST/DeclBase.h"
1111
#include "clang/Analysis/AnalysisDeclContext.h"
12+
#include "clang/Index/USRGeneration.h"
1213
#include "llvm/ADT/STLExtras.h"
14+
#include "llvm/ADT/SmallVector.h"
1315
#include "llvm/ADT/StringExtras.h"
1416
#include "llvm/ADT/StringRef.h"
1517
#include "llvm/Support/FileSystem.h"
@@ -38,6 +40,7 @@ struct Registry {
3840
};
3941

4042
std::vector<Snapshot> Snapshots;
43+
std::string EscapedCPPFileName;
4144
};
4245
} // namespace
4346

@@ -69,7 +72,7 @@ static void checkStatName(const EntryPointStat *M) {
6972
}
7073
}
7174

72-
void EntryPointStat::lockRegistry() {
75+
void EntryPointStat::lockRegistry(llvm::StringRef CPPFileName) {
7376
auto CmpByNames = [](const EntryPointStat *L, const EntryPointStat *R) {
7477
return L->name() < R->name();
7578
};
@@ -78,6 +81,8 @@ void EntryPointStat::lockRegistry() {
7881
enumerateStatVectors(
7982
[](const auto &Stats) { llvm::for_each(Stats, checkStatName); });
8083
StatsRegistry->IsLocked = true;
84+
llvm::raw_string_ostream OS(StatsRegistry->EscapedCPPFileName);
85+
llvm::printEscapedString(CPPFileName, OS);
8186
}
8287

8388
[[maybe_unused]] static bool isRegistered(llvm::StringLiteral Name) {
@@ -144,15 +149,27 @@ static std::vector<llvm::StringLiteral> getStatNames() {
144149
return Ret;
145150
}
146151

152+
static std::string getUSR(const Decl *D) {
153+
llvm::SmallVector<char> Buf;
154+
if (index::generateUSRForDecl(D, Buf)) {
155+
assert(false && "This should never fail");
156+
return AnalysisDeclContext::getFunctionName(D);
157+
}
158+
return llvm::toStringRef(Buf).str();
159+
}
160+
147161
void Registry::Snapshot::dumpAsCSV(llvm::raw_ostream &OS) const {
148162
OS << '"';
163+
llvm::printEscapedString(getUSR(EntryPoint), OS);
164+
OS << "\",\"";
165+
OS << StatsRegistry->EscapedCPPFileName << "\",\"";
149166
llvm::printEscapedString(
150167
clang::AnalysisDeclContext::getFunctionName(EntryPoint), OS);
151-
OS << "\", ";
168+
OS << "\",";
152169
auto PrintAsBool = [&OS](bool B) { OS << (B ? "true" : "false"); };
153-
llvm::interleaveComma(BoolStatValues, OS, PrintAsBool);
154-
OS << ((BoolStatValues.empty() || UnsignedStatValues.empty()) ? "" : ", ");
155-
llvm::interleaveComma(UnsignedStatValues, OS);
170+
llvm::interleave(BoolStatValues, OS, PrintAsBool, ",");
171+
OS << ((BoolStatValues.empty() || UnsignedStatValues.empty()) ? "" : ",");
172+
llvm::interleave(UnsignedStatValues, OS, [&OS](unsigned U) { OS << U; }, ",");
156173
}
157174

158175
static std::vector<bool> consumeBoolStats() {
@@ -181,8 +198,8 @@ void EntryPointStat::dumpStatsAsCSV(llvm::StringRef FileName) {
181198
}
182199

183200
void EntryPointStat::dumpStatsAsCSV(llvm::raw_ostream &OS) {
184-
OS << "EntryPoint, ";
185-
llvm::interleaveComma(getStatNames(), OS);
201+
OS << "USR,File,DebugName,";
202+
llvm::interleave(getStatNames(), OS, [&OS](const auto &a) { OS << a; }, ",");
186203
OS << "\n";
187204

188205
std::vector<std::string> Rows;

0 commit comments

Comments
 (0)