Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clang, exception raised or not depending on compiler options #3247

Closed
m5k8 opened this issue Dec 24, 2022 · 17 comments
Closed

clang, exception raised or not depending on compiler options #3247

m5k8 opened this issue Dec 24, 2022 · 17 comments

Comments

@m5k8
Copy link

m5k8 commented Dec 24, 2022

Isolated problem - test.cc:

    #include <fmt/format.h>
    #include <iostream>

    int main()
    {
        char buf[100];
        char* iterator = buf;
        auto ret = fmt::format_to_n(iterator, sizeof(buf), "Test {}", "OK");
	std::cout << ret.size << std::endl;
    
        return 0;
    }

clang version 14.0.6. Then running compiled executable:
clang++ -I fmt/include -std=c++20 test.cc fmt/src/format.cc - OK
clang++ -I fmt/include -std=c++20 -fomit-frame-pointer test.cc fmt/src/format.cc - OK
clang++ -I fmt/include -std=c++2b test.cc fmt/src/format.cc - OK
clang++ -I fmt/include -std=c++2b -fomit-frame-pointer test.cc fmt/src/format.cc - runtime exception:

terminate called after throwing an instance of 'fmt::v9::format_error'
  what():  argument not found

clang++ -I fmt/include -std=c++2b -fomit-frame-pointer -O2 test.cc fmt/src/format.cc - OK again.

So the outcome depends on selected C++ standard and compiler options. c++2b and option for omitting frame pointer fails, but adding optimization O2 to this makes it work again.
I'm not competent enough to dig into this problem, not sure whether it's fmt library issue or compiler issue.
gcc is fine with all these variants. MSVC also (albeit options do not apply).

Please advise.

@vitaut
Copy link
Contributor

vitaut commented Dec 24, 2022

I wasn't able to repro this on clang++ 14: https://godbolt.org/z/99v59KEhb. Likely a compiler issue on the specific compiler version.

@m5k8
Copy link
Author

m5k8 commented Dec 30, 2022

More tests: problem happens on current Arch Linux, when compiled with clang 14.0.6, using libstdc++.
When using -stdlib=libc++ it's fine. So switching C++ library solves the issue.
When compiled on Debian (still using clang 14.0.6) , it's fine with both libraries. The only difference is that Arch has libstdc++ 6.0.30, Debian has 6.0.25.

@m5k8
Copy link
Author

m5k8 commented Dec 30, 2022

Does godbolt compile the library using the same options as main source file?
Because I've also tried using system-wide installed fmt library, version 9.1.0-2 - and then it works properly.

@vitaut
Copy link
Contributor

vitaut commented Dec 31, 2022

It doesn't but we can force compilation with the same options using FMT_HEADER_ONLY which also doesn't reproduce the issue: https://godbolt.org/z/Mvv3zfPWd

@m5k8
Copy link
Author

m5k8 commented Dec 31, 2022

To the previous code, I've added #define FMT_HEADER_ONLY. So it's not linking anything external.

    $ clang++ -I ./fmt/include fmt.cc -std=c++2b -fomit-frame-pointer ; ./a.out 
    terminate called after throwing an instance of 'fmt::v9::format_error'
      what():  argument not found
    Aborted (core dumped)
    
    # switch to system-wide installed fmt library:
    
    $ clang++ -I /usr/include fmt.cc -std=c++2b -fomit-frame-pointer ; ./a.out 
    7

So proper result.
Let's compare versions:

    $ (cd fmt ; git describe --tags)
    9.1.0-156-g6056e071
    
    $ pacman -Q fmt # system-wide fmt version
    fmt 9.1.0-2

Certainly system lib is somewhat older. Let's switch local git clone to older version, the same as system lib:

    $ (cd fmt; git checkout 9.1.0)
    HEAD is now at a3370119 Update version
    
    $ clang++ -I ./fmt/include fmt.cc -std=c++2b -fomit-frame-pointer ; ./a.out 
    7

Works!

Back to git master:

    $ (cd fmt; git checkout master)
    Previous HEAD position was a3370119 Update version
    Switched to branch 'master'
    Your branch is up to date with 'origin/master'.
    
    $ clang++ -I ./fmt/include fmt.cc -std=c++2b -fomit-frame-pointer ; ./a.out 
    terminate called after throwing an instance of 'fmt::v9::format_error'
      what():  argument not found
    Aborted (core dumped)

Failure again. So, something happens between these commits.
Now some tedious checkout-compile-compare cycle, looking for guilty one:

    $ (cd fmt; git checkout 491c32cbd9ba0cc4b36f7576afcaf6a0a37b0633)
    Previous HEAD position was d2c47c0d Fix broken condition (#3129)
    HEAD is now at 491c32cb Workaround gcc bug 103879
    
    $ clang++ -I ./fmt/include fmt.cc -std=c++2b -fomit-frame-pointer ; ./a.out 
    7
    
    $ (cd fmt; git checkout d2c47c0df23d95e3d7c5def06e5f67f32eb7ffc7)
    Previous HEAD position was 491c32cb Workaround gcc bug 103879
    HEAD is now at d2c47c0d Fix broken condition (#3129)
    
    $ clang++ -I ./fmt/include fmt.cc -std=c++2b -fomit-frame-pointer ; ./a.out 
    terminate called after throwing an instance of 'fmt::v9::format_error'
      what():  argument not found
    Aborted (core dumped)

So here we are, 491c32c -> d2c47c0 causes this problem.

@vitaut
Copy link
Contributor

vitaut commented Dec 31, 2022

Could you post the full stack trace? Also cc @phprus in case he has ideas what might be causing problems in d2c47c0.

@m5k8
Copy link
Author

m5k8 commented Jan 3, 2023

    terminate called after throwing an instance of 'fmt::v9::format_error'
      what():  argument not found
    
    Program received signal SIGABRT, Aborted.
    0x00007ffff7ad764c in ?? () from /usr/lib/libc.so.6
    (gdb) bt
    #0  0x00007ffff7ad764c in ?? () from /usr/lib/libc.so.6
    #1  0x00007ffff7a87958 in raise () from /usr/lib/libc.so.6
    #2  0x00007ffff7a7153d in abort () from /usr/lib/libc.so.6
    #3  0x00007ffff7dd7833 in __gnu_cxx::__verbose_terminate_handler ()
        at /usr/src/debug/gcc/libstdc++-v3/libsupc++/vterminate.cc:95
    #4  0x00007ffff7de3cfc in __cxxabiv1::__terminate (handler=<optimized out>)
        at /usr/src/debug/gcc/libstdc++-v3/libsupc++/eh_terminate.cc:48
    #5  0x00007ffff7de3d69 in std::terminate () at /usr/src/debug/gcc/libstdc++-v3/libsupc++/eh_terminate.cc:58
    #6  0x00007ffff7de3fcd in __cxxabiv1::__cxa_throw (obj=<optimized out>, 
        tinfo=0x555555589ba8 <typeinfo for fmt::v9::format_error>, 
        dest=0x555555557640 <fmt::v9::format_error::~format_error()>)
        at /usr/src/debug/gcc/libstdc++-v3/libsupc++/eh_throw.cc:98
    #7  0x0000555555558a55 in fmt::v9::detail::throw_format_error (message=0x555555580443 "argument not found")
        at ./fmt/include/fmt/format-inl.h:44
    #8  0x00005555555587c8 in fmt::v9::detail::error_handler::on_error (this=0x7fffffffd448, 
        message=0x555555580443 "argument not found") at ./fmt/include/fmt/core.h:648
    #9  0x000055555557448d in fmt::v9::basic_format_parse_context<char, fmt::v9::detail::error_handler>::on_error (
        this=0x7fffffffd448, message=0x555555580443 "argument not found") at ./fmt/include/fmt/core.h:728
    #10 0x00005555555744d1 in fmt::v9::basic_format_parse_context<char, fmt::v9::detail::error_handler>::do_check_arg_id (
        this=0x7fffffffd448, id=0) at ./fmt/include/fmt/core.h:786
    #11 0x0000555555574458 in fmt::v9::basic_format_parse_context<char, fmt::v9::detail::error_handler>::next_arg_id (
        this=0x7fffffffd448) at ./fmt/include/fmt/core.h:708
    #12 0x00005555555723b2 in fmt::v9::detail::vformat_to<char>(fmt::v9::detail::buffer<char>&, fmt::v9::basic_string_view<char>, fmt::v9::basic_format_args<fmt::v9::basic_format_context<std::conditional<std::is_same<char, char>::value, fmt::v9::appender, std::back_insert_iterator<fmt::v9::detail::buffer<char> > >::type, char> >, fmt::v9::detail::locale_ref)::format_handler::on_arg_id() (this=0x7fffffffd440) at ./fmt/include/fmt/format.h:4175
    #13 0x00005555555715a7 in fmt::v9::detail::parse_replacement_field<char, fmt::v9::detail::vformat_to<char>(fmt::v9::detail::buffer<char>&, fmt::v9::basic_string_view<char>, fmt::v9::basic_format_args<fmt::v9::basic_format_context<std::conditional<std::is_same<char, char>::value, fmt::v9::appender, std::back_insert_iterator<fmt::v9::detail::buffer<char> > >::type, char> >, fmt::v9::detail::locale_ref)::format_handler&>(char const*, char const*, fmt::v9::detail::vformat_to<char>(fmt::v9::detail::buffer<char>&, fmt::v9::basic_string_view<char>, fmt::v9::basic_format_args<fmt::v9::basic_format_context<std::conditional<std::is_same<char, char>::value, fmt::v9::appender, std::back_insert_iterator<fmt::v9::detail::buffer<char> > >::type, char> >, fmt::v9::detail::locale_ref)::format_handler&) (begin=0x55555558043e "}", 
        end=0x55555558043f "", handler=...) at ./fmt/include/fmt/core.h:2670
    #14 0x0000555555558141 in fmt::v9::detail::parse_format_string<false, char, fmt::v9::detail::vformat_to<char>(fmt::v9::detail::buffer<char>&, fmt::v9::basic_string_view<char>, fmt::v9::basic_format_args<fmt::v9::basic_format_context<std::conditional<std::is_same<char, char>::value, fmt::v9::appender, std::back_insert_iterator<fmt::v9::detail::buffer<char> > >::type, char> >, fmt::v9::detail::locale_ref)::format_handler>(fmt::v9::basic_string_view<char>, fmt::v9::detail::vformat_to<char>(fmt::v9::detail::buffer<char>&, fmt::v9::basic_string_view<char>, fmt::v9::basic_format_args<fmt::v9::basic_format_context<std::conditional<std::is_same<char, char>::value, fmt::v9::appender, std::back_insert_iterator<fmt::v9::detail::buffer<char> > >::type, char> >, fmt::v9::detail::locale_ref)::format_handler&&) (format_str=..., handler=...)
        at ./fmt/include/fmt/core.h:2705
    #15 fmt::v9::detail::vformat_to<char> (buf=..., fmt=..., args=..., loc=...) at ./fmt/include/fmt/format.h:4214
    #16 0x0000555555557745 in fmt::v9::vformat_to_n<char*, , 0>(char*, unsigned long, fmt::v9::basic_string_view<char>, fmt::v9::basic_format_args<fmt::v9::basic_format_context<fmt::v9::appender, char> >) (out=0x7fffffffd790 "Test \177", 
        n=100, fmt=..., args=...) at ./fmt/include/fmt/core.h:3268
    #17 0x00005555555575a2 in fmt::v9::format_to_n<char*, char const (&) [3], 0> (out=0x7fffffffd790 "Test \177", n=100, 
        fmt=..., args=...) at ./fmt/include/fmt/core.h:3284
    #18 main () at fmt.cc:9

I still think that it could be some compiler error. It happens depending on compiler options used, and only in one exact situation. Generally, works properly in most circumstances. I'll recheck with newer clang (15), but ArchLinux, although bleeding edge as they say, is still on clang14, and I currently have no time budget to fetch and compile all the relevant clang sources. Sometime later.
Still, it's toggled by small change within code, so it's probably good case to catch that library or compiler error.
If your are willing to log remotely into my ststem, I can prepare proper demonstration, because "seeing is believing" :)

@vitaut
Copy link
Contributor

vitaut commented Jan 14, 2023

Looks like a bug in std::is_constant_evaluated because the path that throws the error should only be triggered at compile time:

fmt/include/fmt/core.h

Lines 779 to 784 in 0f42c17

if (detail::is_constant_evaluated() &&
(!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
using context = detail::compile_parse_context<Char>;
if (id >= static_cast<context*>(this)->num_args())
detail::throw_format_error("argument not found");
}

We could exclude the problematic version(s) of clang there as a workaround, similarly to how we do it for gcc < 12.

@phprus
Copy link
Contributor

phprus commented Jan 14, 2023

@vitaut
The error may depend on the version of libstdc++.

@m5k8
What version of libstdc++ are you using?

Info.
Clang 14+ support C++23 if consteval (https://en.cppreference.com/w/cpp/compiler_support).
libstdc++ from gcc-12+ implement std::is_constant_evaluated by if consteval (gcc-mirror/gcc@74d1477#diff-c6b01e66cb8395ef2cd68becb284b73e1bef9ae2d043c04e6fc9d53555993c26, https://github.com/gcc-mirror/gcc/blob/releases/gcc-12.1.0/libstdc++-v3/include/std/type_traits#L3513-L3527).

@phprus
Copy link
Contributor

phprus commented Jan 14, 2023

@vitaut
I think, we can use __builtin_is_constant_evaluated() in problem cases.

@m5k8
Copy link
Author

m5k8 commented Jan 17, 2023

What version of libstdc++ are you using?

Arch Linux distributes libstdc++ within collective gcc-libs: https://archlinux.org/packages/core/x86_64/gcc-libs/

gcc-libs is at version 12.2.0-1 (the same as gcc compiler), libstdc++ is /usr/lib/libstdc++.so.6.0.30

@phprus
Copy link
Contributor

phprus commented Jan 17, 2023

In function

fmt/include/fmt/core.h

Lines 330 to 338 in dfbb952

constexpr FMT_INLINE auto is_constant_evaluated(
bool default_value = false) noexcept -> bool {
#ifdef __cpp_lib_is_constant_evaluated
ignore_unused(default_value);
return std::is_constant_evaluated();
#else
return default_value;
#endif
}

Replace return std::is_constant_evaluated(); with return __builtin_is_constant_evaluated(); and retry tests.

@phprus
Copy link
Contributor

phprus commented Jan 17, 2023

And can you test fmt with and without this patch on clang-15?

@m5k8
Copy link
Author

m5k8 commented Jan 17, 2023

Replace return std::is_constant_evaluated(); with return __builtin_is_constant_evaluated(); and retry tests.

After the change - issue is solver and it works properly!

And can you test fmt with and without this patch on clang-15?

I'll test, but have to install clang15 yet, so results in a moment or more like two moments.

@phprus
Copy link
Contributor

phprus commented Jan 20, 2023

@m5k8
Do you have any news?

@phprus
Copy link
Contributor

phprus commented Jan 21, 2023

@m5k8
Test PR #3281 please.

@m5k8
Copy link
Author

m5k8 commented Jan 21, 2023

Test PR #3281 please.

This fixes the issue with clang14. I also have tested with clang15, and it's not needed - previous code (without the above fix) works properly. So it was only clang14 issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants