Skip to content

Commit c40fd34

Browse files
[NFC] Make format() more amenable to format attributes
This change modifies the implementation of the format() function so that vendor forks committed to building with compilers that support __attribute__((format)) on non-variadic functions can check the format() function with it. rdar://84571523
1 parent 5badd22 commit c40fd34

File tree

7 files changed

+770
-27
lines changed

7 files changed

+770
-27
lines changed

llvm/include/llvm/Support/Format.h

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,63 @@
3333

3434
namespace llvm {
3535

36+
/// Utility class that parses printf-style format strings to yield the expected
37+
/// C type(s) of each specifier. This class is used to verify that a format
38+
/// string unknown at compile-time is equivalent to another format string (which
39+
/// itself is hopefully known at compile-time).
40+
class PrintfStyleFormatReader {
41+
public:
42+
enum SpecifierType : char {
43+
ST_EndOfFormatString,
44+
ST_Unknown,
45+
ST_WideChar,
46+
ST_Int,
47+
ST_Long,
48+
ST_LongLong,
49+
ST_IntMax,
50+
ST_Size,
51+
ST_Ptrdiff,
52+
ST_Double,
53+
ST_LongDouble,
54+
ST_CString,
55+
ST_WideCString,
56+
ST_VoidPointer,
57+
ST_Count_Char,
58+
ST_Count_Short,
59+
ST_Count_Int,
60+
ST_Count_Long,
61+
ST_Count_LongLong,
62+
ST_Count_IntMax,
63+
ST_Count_Size,
64+
ST_Count_Ptrdiff
65+
};
66+
67+
private:
68+
const char *Fmt;
69+
llvm::SmallVector<SpecifierType, 3> SpecifierQueue;
70+
71+
void refillSpecifierQueue();
72+
73+
public:
74+
/// Verify that the format specifiers in \p Fmt consume no more arguments than
75+
/// those in \p Expected, and that all consumed arguments have a compatible
76+
/// type. If \p Fmt is compatible with \p Expected in this way, \p Fmt is
77+
/// returned. Otherwise, \p Expected is returned.
78+
static const char *ensureCompatible(const char *Expected, const char *Fmt);
79+
80+
PrintfStyleFormatReader(const char *Fmt) : Fmt(Fmt) {}
81+
82+
SpecifierType nextSpecifier() {
83+
if (SpecifierQueue.empty())
84+
refillSpecifierQueue();
85+
return SpecifierQueue.pop_back_val();
86+
}
87+
};
88+
3689
/// This is a helper class used for handling formatted output. It is the
3790
/// abstract base class of a templated derived class.
3891
class format_object_base {
3992
protected:
40-
const char *Fmt;
4193
~format_object_base() = default; // Disallow polymorphic deletion.
4294
format_object_base(const format_object_base &) = default;
4395
virtual void home(); // Out of line virtual method.
@@ -46,7 +98,7 @@ class format_object_base {
4698
virtual int snprint(char *Buffer, unsigned BufferSize) const = 0;
4799

48100
public:
49-
format_object_base(const char *fmt) : Fmt(fmt) {}
101+
format_object_base() = default;
50102

51103
/// Format the object into the specified buffer. On success, this returns
52104
/// the length of the formatted string. If the buffer is too small, this
@@ -86,28 +138,27 @@ struct validate_format_parameters<Arg, Args...> {
86138
};
87139
template <> struct validate_format_parameters<> {};
88140

89-
template <typename... Ts>
90-
class format_object final : public format_object_base {
91-
std::tuple<Ts...> Vals;
92-
93-
template <std::size_t... Is>
94-
int snprint_tuple(char *Buffer, unsigned BufferSize,
95-
std::index_sequence<Is...>) const {
141+
template <typename... Ts> auto format_capture(const char *Fmt, Ts... Vals) {
142+
validate_format_parameters<Ts...>();
143+
return [=](char *Buffer, unsigned BufferSize) {
96144
#ifdef _MSC_VER
97-
return _snprintf(Buffer, BufferSize, Fmt, std::get<Is>(Vals)...);
145+
return _snprintf(Buffer, BufferSize, Fmt, Vals...);
98146
#else
99-
return snprintf(Buffer, BufferSize, Fmt, std::get<Is>(Vals)...);
147+
return snprintf(Buffer, BufferSize, Fmt, Vals...);
100148
#endif
101-
}
149+
};
150+
}
151+
152+
template <typename... Ts>
153+
class format_object final : public format_object_base {
154+
decltype(format_capture<Ts...>("", std::declval<Ts>()...)) Format;
102155

103156
public:
104-
format_object(const char *fmt, const Ts &... vals)
105-
: format_object_base(fmt), Vals(vals...) {
106-
validate_format_parameters<Ts...>();
107-
}
157+
format_object(const char *Fmt, const Ts &...vals)
158+
: Format(format_capture(Fmt, vals...)) {}
108159

109160
int snprint(char *Buffer, unsigned BufferSize) const override {
110-
return snprint_tuple(Buffer, BufferSize, std::index_sequence_for<Ts...>());
161+
return Format(Buffer, BufferSize);
111162
}
112163
};
113164

llvm/lib/Support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ add_llvm_component_library(LLVMSupport
156156
FileUtilities.cpp
157157
FileOutputBuffer.cpp
158158
FoldingSet.cpp
159+
Format.cpp
159160
FormattedStream.cpp
160161
FormatVariadic.cpp
161162
GlobPattern.cpp

0 commit comments

Comments
 (0)