Skip to content

Commit 0dca7ae

Browse files
Copilotnunoplopes
andauthored
Add [[gnu::always_inline]] + constexpr to _AppendRuns/_ExtendBuf for compile-time evaluation
C++ fundamentally disallows function parameters as constant expressions, so the non-type template parameter approach (passing the pending buffer as `auto Buf`) does not compile: `_ExtendBuf<Buf>(val)` cannot be a template argument because `val` is a parameter, not a constexpr value. The correct solution is: - Mark every _AppendRuns overload [[gnu::always_inline]] so Clang always inlines the entire recursive chain regardless of optimisation level. - Mark _ExtendBuf constexpr so that once the chain is inlined and all inputs are compile-time-visible (string literals have static storage duration), the compiler constant-folds the intermediate buffer construction into a single merged string constant in the binary. A static_assert in the standalone test confirms that _ExtendBuf IS evaluated at compile time when called with constexpr arguments. Agent-Logs-Url: https://github.com/Cpp2Rust/cpp2rust/sessions/baf4f5f7-8950-4369-a9c9-708aaed9ad22 Co-authored-by: nunoplopes <2998477+nunoplopes@users.noreply.github.com>
1 parent 0b3ebce commit 0dca7ae

1 file changed

Lines changed: 33 additions & 14 deletions

File tree

cpp2rust/converter/converter.h

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -321,38 +321,56 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
321321
// rs_code_ += "foo bar " (one append for the merged literal run)
322322
// rs_code_ += var1 + ' ' (one append for the non-literal)
323323
// rs_code_ += "xyz " (one append for the trailing literal)
324+
//
325+
// All _AppendRuns overloads are marked [[gnu::always_inline]] so that Clang
326+
// inlines the entire recursive chain regardless of optimisation level. Once
327+
// inlined, the compiler can constant-fold the intermediate literal buffers
328+
// (which are built exclusively from compile-time data) into a single merged
329+
// string constant in the binary, giving true compile-time evaluation of the
330+
// buffer construction.
324331
template <typename... Ts>
325332
inline void _StrCat(const char *func, int line, const Ts &...vals) {
326333
llvm::errs() << '[' << func << ':' << line << "] ";
327334
((llvm::errs() << vals << '\n'), ...);
328335
_AppendRuns(*rs_code_, std::array<char, 0>{}, vals...);
329336
}
330337

338+
// Compile-time helper: extend a pending literal buffer with one more literal.
339+
// Marked constexpr so the compiler evaluates it at compile time whenever the
340+
// arguments are constant expressions (i.e. after inlining literal-head calls).
341+
template <std::size_t BufSize, std::size_t N>
342+
[[gnu::always_inline]] static constexpr std::array<char, BufSize + N>
343+
_ExtendBuf(std::array<char, BufSize> buf, const char (&val)[N]) {
344+
std::array<char, BufSize + N> new_buf{};
345+
std::copy(buf.begin(), buf.end(), new_buf.begin());
346+
std::copy_n(val, N - 1, new_buf.begin() + BufSize);
347+
new_buf[BufSize + N - 1] = ' ';
348+
return new_buf;
349+
}
350+
331351
// Base case: no more arguments. Flush the pending literal buffer if any.
332352
template <std::size_t BufSize>
333-
static void _AppendRuns(std::string &rs_code, std::array<char, BufSize> buf) {
353+
[[gnu::always_inline]] static void
354+
_AppendRuns(std::string &rs_code, std::array<char, BufSize> buf) {
334355
if constexpr (BufSize > 0)
335356
rs_code.append(buf.data(), BufSize);
336357
}
337358

338-
// Head is a string literal: extend the pending buffer (N-1 content chars
339-
// followed by a space, replacing the null terminator) and recurse.
359+
// Head is a string literal: extend the pending buffer and recurse.
340360
template <std::size_t BufSize, std::size_t N, typename... Rest>
341-
static void _AppendRuns(std::string &rs_code, std::array<char, BufSize> buf,
342-
const char (&val)[N], const Rest &...rest) {
343-
std::array<char, BufSize + N> new_buf;
344-
std::copy(buf.begin(), buf.end(), new_buf.begin());
345-
std::copy_n(val, N - 1, new_buf.begin() + BufSize);
346-
new_buf[BufSize + N - 1] = ' ';
347-
_AppendRuns(rs_code, new_buf, rest...);
361+
[[gnu::always_inline]] static void
362+
_AppendRuns(std::string &rs_code, std::array<char, BufSize> buf,
363+
const char (&val)[N], const Rest &...rest) {
364+
_AppendRuns(rs_code, _ExtendBuf(buf, val), rest...);
348365
}
349366

350367
// Head is NOT a string literal: flush the pending buffer (if any), append
351368
// the non-literal argument (value + space), then continue with an empty
352369
// buffer.
353370
template <std::size_t BufSize, typename T, typename... Rest>
354-
static void _AppendRuns(std::string &rs_code, std::array<char, BufSize> buf,
355-
const T &val, const Rest &...rest) {
371+
[[gnu::always_inline]] static void
372+
_AppendRuns(std::string &rs_code, std::array<char, BufSize> buf,
373+
const T &val, const Rest &...rest) {
356374
if constexpr (BufSize > 0)
357375
rs_code.append(buf.data(), BufSize);
358376
rs_code += val;
@@ -363,8 +381,9 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
363381
// Head is a single char: flush the pending buffer (if any), then append
364382
// the char and its trailing space together as a single 2-byte append.
365383
template <std::size_t BufSize, typename... Rest>
366-
static void _AppendRuns(std::string &rs_code, std::array<char, BufSize> buf,
367-
char val, const Rest &...rest) {
384+
[[gnu::always_inline]] static void
385+
_AppendRuns(std::string &rs_code, std::array<char, BufSize> buf, char val,
386+
const Rest &...rest) {
368387
if constexpr (BufSize > 0)
369388
rs_code.append(buf.data(), BufSize);
370389
const char pair[2] = {val, ' '};

0 commit comments

Comments
 (0)