cppfront icon indicating copy to clipboard operation
cppfront copied to clipboard

[BUG] Varargs breaks with non-PODs

Open JohelEGP opened this issue 2 years ago • 6 comments

Title: Varargs breaks with non-PODs.

Description:

With Clang, an error stopped me from having a broken program: https://cpp1.godbolt.org/z/xW8qjhnfP. With MSVC, this is actually just a warning. With GCC, there's no warning at all.

Minimal reproducer (https://cpp2.godbolt.org/z/oPfvj3cEz):

#include <ranges>
using namespace std::views;
using std::string;
main: () = {
  (base_types := 0.iota(), //
   base_targs := :std::vector = ("".string().repeat(1), ("<(42)$>").repeat(1 << 16)))
    for zip(base_types, base_targs) do (b) ("this: (b.get<0>())$(b.get<1>())$ = ();").clear();
}
Commands:
cppfront main.cpp2
clang++18 -std=c++23 -stdlib=libc++ -lc++abi -pedantic-errors -Wall -Wextra -Wconversion -Werror=unused-result -I . main.cpp

Expected result:

This apparently broken function not to exist: https://github.com/hsutter/cppfront/blob/8a9346033ad5ec4608ebd8292539e0718f9d991b/include/cpp2util.h#L872

Actual result and error:

Cpp2 lowered to Cpp1:


//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"



//=== Cpp2 type definitions and function declarations ===========================

#include <ranges>
using namespace std::views;
using std::string;
auto main() -> int;
  

//=== Cpp2 function definitions =================================================


auto main() -> int{
{
auto const& base_types = CPP2_UFCS_0(iota, 0);
auto const& base_targs = std::vector{CPP2_UFCS(repeat, CPP2_UFCS_0(string, ""), 1), CPP2_UFCS(repeat, ("<" + cpp2::to_string(42) + ">"), 1 << 16)};
                           //

    for ( auto const& b : zip(base_types, base_targs) ) CPP2_UFCS_0(clear, ("this: " + cpp2::to_string(CPP2_UFCS_TEMPLATE_0(get, (<0>), b)) + cpp2::to_string(CPP2_UFCS_TEMPLATE_0(get, (<1>), b)) + " = ();")); 
}
}
Output:
main.cpp2:7:159: error: cannot pass object of non-trivial type 'const std::ranges::repeat_view<std::string, int>' through variadic function; call will abort at runtime [-Wnon-pod-varargs]
    7 |     for ( auto const& b : zip(base_types, base_targs) ) CPP2_UFCS_0(clear, ("this: " + cpp2::to_string(CPP2_UFCS_TEMPLATE_0(get, (<0>), b)) + cpp2::to_string(CPP2_UFCS_TEMPLATE_0(get, (<1>), b)) + " = ();")); 
      |                                                                                                                                                               ^
raw.githubusercontent.com/hsutter/cppfront/main/include/cpp2util.h:810:56: note: expanded from macro 'CPP2_UFCS_TEMPLATE_0'
  810 | #define CPP2_UFCS_TEMPLATE_0(FUNCNAME,TEMPARGS,PARAM1) \
      |                                                        ^
1 error generated.

JohelEGP avatar Nov 15 '23 01:11 JohelEGP

It seems like GCC executes just fine: https://cpp1.godbolt.org/z/jMz8YGnE8.

JohelEGP avatar Nov 15 '23 01:11 JohelEGP

This simpler reproducer illustrates the problem.

Minimal reproducer (https://cpp2.godbolt.org/z/9cPPe6Tb9):

main: (args) = {
  _ = "(args)$";
}
Commands:
cppfront main.cpp2
clang++18 -std=c++23 -stdlib=libc++ -lc++abi -pedantic-errors -Wall -Wextra -Wconversion -Werror=unused-result -I . main.cpp
Cpp2 lowered to Cpp1:


//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"

#line 1 "/app/example.cpp2"


//=== Cpp2 type definitions and function declarations ===========================

#line 1 "/app/example.cpp2"
auto main(int const argc_, char** argv_) -> int;

//=== Cpp2 function definitions =================================================

#line 1 "/app/example.cpp2"
auto main(int const argc_, char** argv_) -> int{
  auto const args = cpp2::make_args(argc_, argv_); 
#line 2 "/app/example.cpp2"
  static_cast<void>(cpp2::to_string(args));
}
Output:
main.cpp2:2:37: error: cannot pass object of non-trivial type 'const args_t' through variadic function; call will abort at runtime [-Wnon-pod-varargs]
    2 |   static_cast<void>(cpp2::to_string(args));
      |                                     ^
1 error generated.

JohelEGP avatar Nov 22 '23 14:11 JohelEGP

I think this means that it's falling back to the "this didn't work, need to make a function for this" case, which really should be a build error, not just a runtime failure.

inline auto to_string(...) -> std::string
{
    return "(customize me - no cpp2::to_string overload exists for this type)";
}

gregmarr avatar Nov 22 '23 15:11 gregmarr

LLVM 18 works fine if I -D__cpp_lib_format. Otherwise, for some reason, the varargs overload is chosen. See https://cpp2.godbolt.org/z/zeqTrhbd1:

main: () = {
  std::cout << "(0:>23)$\n";
  std::cout << "(:std::vector=(0,1,2);:>23)$";
}

JohelEGP avatar Nov 25 '23 14:11 JohelEGP

Thanks. Do you have a suggested fix? For example, do you prefer removing that fallback overload, changing it to (auto&&...), or something else?

hsutter avatar Dec 21 '23 18:12 hsutter

I would be in favor of making it a compile error, especially if we can get some decent error messages out of the error.

gregmarr avatar Dec 21 '23 18:12 gregmarr