From ea251536d55f493d9eb4bb18fb027709c4d39509 Mon Sep 17 00:00:00 2001 From: Tomohiro Kashiwada Date: Mon, 13 Oct 2025 18:09:42 +0900 Subject: [PATCH] [LLVM][Support] Allow `char[N]` parameters in `llvm::format` (#159541) Cygwin builds are currently broken after #157312, which effectively reverted #138117. The root cause is that Cygwin defines `DL_info::dli_fname` as `char[N]`, which is not a valid parameter type for `llvm::format`. This patch allows `llvm::format` to accept `char[N]` by decaying it to `const char *`. As a result, string literals are also accepted without an explicit cast. Other array types remain rejected: - Wide/unicode character arrays (e.g., `wchar_t[N]`) are not supported, as LLVM does not use them and they are less compatible with platform's `printf` implementations. - Non-character arrays (e.g., `int[N]`) are also rejected, since passing such arrays to `printf` is meaningless. --- llvm/include/llvm/Support/Format.h | 15 ++++++- llvm/unittests/Support/CMakeLists.txt | 1 + llvm/unittests/Support/Format.cpp | 56 +++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 llvm/unittests/Support/Format.cpp diff --git a/llvm/include/llvm/Support/Format.h b/llvm/include/llvm/Support/Format.h index 34b224dba540..b549341a40e7 100644 --- a/llvm/include/llvm/Support/Format.h +++ b/llvm/include/llvm/Support/Format.h @@ -78,9 +78,20 @@ public: /// printed, this synthesizes the string into a temporary buffer provided and /// returns whether or not it is big enough. +namespace detail { +template struct decay_if_c_char_array { + using type = T; +}; +template struct decay_if_c_char_array { + using type = const char *; +}; +template +using decay_if_c_char_array_t = typename decay_if_c_char_array::type; +} // namespace detail + template class format_object final : public format_object_base { - std::tuple Vals; + std::tuple...> Vals; template int snprint_tuple(char *Buffer, unsigned BufferSize, @@ -96,7 +107,7 @@ public: format_object(const char *fmt, const Ts &... vals) : format_object_base(fmt), Vals(vals...) { static_assert( - (std::is_scalar_v && ...), + (std::is_scalar_v> && ...), "format can't be used with non fundamental / non pointer type"); } diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt index 25efa00c5abf..21f10eb610f1 100644 --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -44,6 +44,7 @@ add_llvm_unittest(SupportTests ExtensibleRTTITest.cpp FileCollectorTest.cpp FileOutputBufferTest.cpp + Format.cpp FormatVariadicTest.cpp FSUniqueIDTest.cpp GenericDomTreeTest.cpp diff --git a/llvm/unittests/Support/Format.cpp b/llvm/unittests/Support/Format.cpp new file mode 100644 index 000000000000..c4e421fe9dc4 --- /dev/null +++ b/llvm/unittests/Support/Format.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/Format.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +template +std::string printToString(unsigned MaxN, FormatTy &&Fmt) { + std::vector Dst(MaxN + 2); + int N = Fmt.snprint(Dst.data(), Dst.size()); + Dst.back() = 0; + return N < 0 ? "" : Dst.data(); +} + +template +constexpr bool checkDecayTypeEq(const Arg &arg) { + return std::is_same_v, Expected>; +} + +TEST(Format, DecayIfCCharArray) { + char Array[] = "Array"; + const char ConstArray[] = "ConstArray"; + char PtrBuf[] = "Ptr"; + char *Ptr = PtrBuf; + const char *PtrToConst = "PtrToConst"; + + EXPECT_EQ(" Literal", printToString(20, format("%15s", "Literal"))); + EXPECT_EQ(" Array", printToString(20, format("%15s", Array))); + EXPECT_EQ(" ConstArray", printToString(20, format("%15s", ConstArray))); + EXPECT_EQ(" Ptr", printToString(20, format("%15s", Ptr))); + EXPECT_EQ(" PtrToConst", printToString(20, format("%15s", PtrToConst))); + + EXPECT_TRUE(checkDecayTypeEq("Literal")); + EXPECT_TRUE(checkDecayTypeEq(Array)); + EXPECT_TRUE(checkDecayTypeEq(ConstArray)); + EXPECT_TRUE(checkDecayTypeEq(Ptr)); + EXPECT_TRUE(checkDecayTypeEq(PtrToConst)); + EXPECT_TRUE(checkDecayTypeEq(PtrToConst[0])); + EXPECT_TRUE( + checkDecayTypeEq(static_cast("Literal"))); + + wchar_t WCharArray[] = L"WCharArray"; + EXPECT_TRUE(checkDecayTypeEq(WCharArray)); + EXPECT_TRUE(checkDecayTypeEq(WCharArray[0])); +} + +} // namespace