Skip to content

build break with clang 20 - specializations for std::is_floating_point is UB (undefined behavior) #4417

Description

@peter-joo

This is the problematic fmt version and code locations:
https://github.com/fmtlib/fmt/blob/11.1.4/test/format-impl-test.cc#L310
https://github.com/fmtlib/fmt/blob/11.1.4/test/format-impl-test.cc#L318

This is what clang 20.1.3 reports:

test/format-impl-test.cc:310:20: error: 'is_floating_point' cannot be specialized: Users are not allowed to specialize this standard library entity [-Winvalid-specialization]
  310 | template <> struct is_floating_point<double_double> : std::true_type {};
      |                    ^
/usr/bin/../include/c++/v1/__type_traits/is_floating_point.h:30:29: note: marked 'no_specializations' here
   30 | struct _LIBCPP_TEMPLATE_VIS _LIBCPP_NO_SPECIALIZATIONS is_floating_point
      |                             ^
/usr/bin/../include/c++/v1/__config:1177:9: note: expanded from macro '_LIBCPP_NO_SPECIALIZATIONS'
 1177 |       [[_Clang::__no_specializations__("Users are not allowed to specialize this standard library entity")]]
      |         ^
test/format-impl-test.cc:318:20: error: 'is_floating_point' cannot be specialized: Users are not allowed to specialize this standard library entity [-Winvalid-specialization]
  318 | template <> struct is_floating_point<slow_float> : std::true_type {};
      |                    ^
/usr/bin/../include/c++/v1/__type_traits/is_floating_point.h:30:29: note: marked 'no_specializations' here
   30 | struct _LIBCPP_TEMPLATE_VIS _LIBCPP_NO_SPECIALIZATIONS is_floating_point
      |                             ^
/usr/bin/../include/c++/v1/__config:1177:9: note: expanded from macro '_LIBCPP_NO_SPECIALIZATIONS'
 1177 |       [[_Clang::__no_specializations__("Users are not allowed to specialize this standard library entity")]]
      |         ^
2 errors generated.

This is how I triggered the build:

cmake -S ./fmtlib_src                    \
      -B ./fmtlib_build                  \
      -G Ninja                           \
      -DCMAKE_BUILD_TYPE=Release         \
      -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
      -DFMT_TEST=ON

The compile_commands.json holds this command:

clang++ -DFMT_HEADER_ONLY=1 -DGTEST_HAS_STD_WSTRING=1 -D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=1 -Ifmtlib_src/include -isystem fmtlib_src/test/gtest/. -O3 -DNDEBUG -fvisibility=hidden -fvisibility-inlines-hidden -o test/CMakeFiles/format-impl-test.dir/format-impl-test.cc.o -c fmtlib_src/test/format-impl-test.cc

This is an other reference, saying that by specializing this is_floating_point standard library entity you actually create an UB:
https://en.cppreference.com/w/cpp/types/is_floating_point

If the program adds specializations for std::is_floating_point the behavior is undefined.

In the standard, C++20, for example it is mentioned in general:
https://isocpp.org/files/papers/N4860.pdf

chapter 16.5.4.2.1 Namespace std, page 480
1 Unless otherwise specified, the behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std.
2 Unless explicitly prohibited, a program may add a template specialization for any standard library class template to namespace std provided that (a) the added declaration depends on at least one program-defined type and (b) the specialization meets the standard library requirements for the original template.
3 The behavior of a C++ program is undefined if it declares an explicit or partial specialization of any standard library variable template, except where explicitly permitted by the specification of that variable template.
4 The behavior of a C++ program is undefined if it declares
(4.1) — an explicit specialization of any member function of a standard library class template, or
(4.2) — an explicit specialization of any member function template of a standard library class or class template, or
(4.3) — an explicit or partial specialization of any member class template of a standard library class or class template, or
(4.4) — a deduction guide for any standard library class template.
5 A program may explicitly instantiate a class template defined in the standard library only if the declaration
(a) depends on the name of at least one program-defined type
and (b) the instantiation meets the standard
library requirements for the original template.

Obviously, both double_double and slow_float are UDTs (=user-defined types = program-defined types) so paragraph 5) may exempt the rule:

A program may explicitly instantiate a class template defined in the standard library only if the declaration depends on the name of at least one program-defined type.

But such exemption are in direct contradiction with:

  • the rule stated in cppreference, of course, stated above
  • clang's implementation, which rejects compiling fmt

Also, pay attention to the Extending the namespace std section at:
https://en.cppreference.com/w/cpp/language/extending_std

Thanks in advance :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions